410 open in side panel (#10363)
Closes https://github.com/twentyhq/core-team-issues/issues/410 - Added `openRecordIn` column in the `view` entity, which is set to `SIDE_PANEL` by default - Created a new option inside the view option dropdown to be able to set `openRecordIn` - Updated all record show page openings to reflect the setting behavior - For `workflow`, `workflowVersion` and `workflowRun` (what I call workflow objects), we want the default view `openRecordIn` to be set to `RECORD_PAGE`. When seeding the views for the new workspaces, we set `openRecordIn` to `RECORD_PAGE` for workflow objects. Since the workflow objects views `openRecordIn` will be set to the default value `SIDE_PANEL` for the existing workspaces when the sync metadata runs, I created a script to run in the 0.43 update to update this value. - Updated `closeCommandMenu` because of problems introduced by the animate presence wrapper around the command menu. We now reset the states at the end of the animation. Note: We want to be able to open all workflow objects pages in the side panel, but this requires some refactoring of the workflow module. For now @Bonapara wanted to allow the possibility to change the `openRecordIn` setting to `SIDE_PANEL` even for the workflows even if it's buggy and not ready for the moment. Since this is an experimental feature, it shouldn't cause too many problems.
This commit is contained in:
@ -48,7 +48,11 @@ export const CommandMenuContainer = ({
|
||||
}: {
|
||||
children: React.ReactNode;
|
||||
}) => {
|
||||
const { toggleCommandMenu, closeCommandMenu } = useCommandMenu();
|
||||
const {
|
||||
toggleCommandMenu,
|
||||
closeCommandMenu,
|
||||
onCommandMenuCloseAnimationComplete,
|
||||
} = useCommandMenu();
|
||||
|
||||
const isCommandMenuOpened = useRecoilValue(isCommandMenuOpenedState);
|
||||
|
||||
@ -98,7 +102,7 @@ export const CommandMenuContainer = ({
|
||||
>
|
||||
<ActionMenuContext.Provider
|
||||
value={{
|
||||
isInRightDrawer: false,
|
||||
isInRightDrawer: true,
|
||||
onActionExecutedCallback: ({ key }) => {
|
||||
if (
|
||||
key !== RecordAgnosticActionsKey.SEARCH_RECORDS &&
|
||||
@ -121,7 +125,10 @@ export const CommandMenuContainer = ({
|
||||
<RunWorkflowRecordAgnosticActionMenuEntriesSetter />
|
||||
)}
|
||||
<ActionMenuConfirmationModals />
|
||||
<AnimatePresence mode="wait">
|
||||
<AnimatePresence
|
||||
mode="wait"
|
||||
onExitComplete={onCommandMenuCloseAnimationComplete}
|
||||
>
|
||||
{isCommandMenuOpened && (
|
||||
<StyledCommandMenu
|
||||
data-testid="command-menu"
|
||||
|
||||
@ -34,16 +34,17 @@ export const CommandMenuContextChipGroupsWithRecordSelection = ({
|
||||
/>
|
||||
));
|
||||
|
||||
const recordSelectionContextChip = totalCount
|
||||
? {
|
||||
text: getSelectedRecordsContextText(
|
||||
objectMetadataItem,
|
||||
records,
|
||||
totalCount,
|
||||
),
|
||||
Icons: Avatars,
|
||||
}
|
||||
: undefined;
|
||||
const recordSelectionContextChip =
|
||||
totalCount && records.length > 0
|
||||
? {
|
||||
text: getSelectedRecordsContextText(
|
||||
objectMetadataItem,
|
||||
records,
|
||||
totalCount,
|
||||
),
|
||||
Icons: Avatars,
|
||||
}
|
||||
: undefined;
|
||||
|
||||
const contextChipsWithRecordSelection = [
|
||||
recordSelectionContextChip,
|
||||
|
||||
@ -21,7 +21,7 @@ export const CommandMenuContextRecordChip = ({
|
||||
instanceId,
|
||||
});
|
||||
|
||||
if (loading || !totalCount) {
|
||||
if (loading || !totalCount || records.length === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
@ -192,6 +192,7 @@ describe('useCommandMenu', () => {
|
||||
|
||||
act(() => {
|
||||
result.current.commandMenu.goBackFromCommandMenu();
|
||||
result.current.commandMenu.onCommandMenuCloseAnimationComplete();
|
||||
});
|
||||
|
||||
expect(result.current.commandMenuNavigationStack).toEqual([]);
|
||||
|
||||
@ -74,31 +74,33 @@ export const useCommandMenu = () => {
|
||||
);
|
||||
|
||||
const closeCommandMenu = useRecoilCallback(
|
||||
({ snapshot, set }) =>
|
||||
({ set }) =>
|
||||
() => {
|
||||
const isCommandMenuOpened = snapshot
|
||||
.getLoadable(isCommandMenuOpenedState)
|
||||
.getValue();
|
||||
set(isCommandMenuOpenedState, false);
|
||||
},
|
||||
[],
|
||||
);
|
||||
|
||||
if (isCommandMenuOpened) {
|
||||
resetContextStoreStates('command-menu');
|
||||
resetContextStoreStates('command-menu-previous');
|
||||
const onCommandMenuCloseAnimationComplete = useRecoilCallback(
|
||||
({ set }) =>
|
||||
() => {
|
||||
resetContextStoreStates('command-menu');
|
||||
resetContextStoreStates('command-menu-previous');
|
||||
|
||||
set(viewableRecordIdState, null);
|
||||
set(commandMenuPageState, CommandMenuPages.Root);
|
||||
set(commandMenuPageInfoState, {
|
||||
title: undefined,
|
||||
Icon: undefined,
|
||||
});
|
||||
set(isCommandMenuOpenedState, false);
|
||||
set(commandMenuSearchState, '');
|
||||
set(commandMenuNavigationStackState, []);
|
||||
resetSelectedItem();
|
||||
set(hasUserSelectedCommandState, false);
|
||||
goBackToPreviousHotkeyScope();
|
||||
set(viewableRecordIdState, null);
|
||||
set(commandMenuPageState, CommandMenuPages.Root);
|
||||
set(commandMenuPageInfoState, {
|
||||
title: undefined,
|
||||
Icon: undefined,
|
||||
});
|
||||
set(isCommandMenuOpenedState, false);
|
||||
set(commandMenuSearchState, '');
|
||||
set(commandMenuNavigationStackState, []);
|
||||
resetSelectedItem();
|
||||
set(hasUserSelectedCommandState, false);
|
||||
goBackToPreviousHotkeyScope();
|
||||
|
||||
emitRightDrawerCloseEvent();
|
||||
}
|
||||
emitRightDrawerCloseEvent();
|
||||
},
|
||||
[goBackToPreviousHotkeyScope, resetContextStoreStates, resetSelectedItem],
|
||||
);
|
||||
@ -109,7 +111,10 @@ export const useCommandMenu = () => {
|
||||
page,
|
||||
pageTitle,
|
||||
pageIcon,
|
||||
}: CommandMenuNavigationStackItem) => {
|
||||
resetNavigationStack = false,
|
||||
}: CommandMenuNavigationStackItem & {
|
||||
resetNavigationStack?: boolean;
|
||||
}) => {
|
||||
set(commandMenuPageState, page);
|
||||
set(commandMenuPageInfoState, {
|
||||
title: pageTitle,
|
||||
@ -120,10 +125,14 @@ export const useCommandMenu = () => {
|
||||
.getLoadable(commandMenuNavigationStackState)
|
||||
.getValue();
|
||||
|
||||
set(commandMenuNavigationStackState, [
|
||||
...currentNavigationStack,
|
||||
{ page, pageTitle, pageIcon },
|
||||
]);
|
||||
if (resetNavigationStack) {
|
||||
set(commandMenuNavigationStackState, [{ page, pageTitle, pageIcon }]);
|
||||
} else {
|
||||
set(commandMenuNavigationStackState, [
|
||||
...currentNavigationStack,
|
||||
{ page, pageTitle, pageIcon },
|
||||
]);
|
||||
}
|
||||
openCommandMenu();
|
||||
};
|
||||
},
|
||||
@ -248,6 +257,7 @@ export const useCommandMenu = () => {
|
||||
? t`New ${capitalizedObjectNameSingular}`
|
||||
: capitalizedObjectNameSingular,
|
||||
pageIcon: Icon,
|
||||
resetNavigationStack: true,
|
||||
});
|
||||
};
|
||||
},
|
||||
@ -315,6 +325,7 @@ export const useCommandMenu = () => {
|
||||
return {
|
||||
openRootCommandMenu,
|
||||
closeCommandMenu,
|
||||
onCommandMenuCloseAnimationComplete,
|
||||
navigateCommandMenu,
|
||||
navigateCommandMenuHistory,
|
||||
goBackFromCommandMenu,
|
||||
|
||||
@ -7,6 +7,7 @@ import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadat
|
||||
import { AGGREGATE_OPERATIONS } from '@/object-record/record-table/constants/AggregateOperations';
|
||||
import { prefetchViewsState } from '@/prefetch/states/prefetchViewsState';
|
||||
import { AppPath } from '@/types/AppPath';
|
||||
import { ViewOpenRecordInType } from '@/views/types/ViewOpenRecordInType';
|
||||
import { ViewType } from '@/views/types/ViewType';
|
||||
import { getCompanyObjectMetadataItem } from '~/testing/mock-data/companies';
|
||||
import { generatedMockObjectMetadataItems } from '~/testing/mock-data/generatedMockObjectMetadataItems';
|
||||
@ -38,6 +39,7 @@ const renderHooks = ({
|
||||
type: ViewType.Table,
|
||||
key: null,
|
||||
isCompact: false,
|
||||
openRecordIn: ViewOpenRecordInType.SIDE_PANEL,
|
||||
viewFields: [],
|
||||
viewGroups: [],
|
||||
viewSorts: [],
|
||||
|
||||
@ -1,9 +1,13 @@
|
||||
import { AvatarChip, AvatarChipVariant } from 'twenty-ui';
|
||||
|
||||
import { useCommandMenu } from '@/command-menu/hooks/useCommandMenu';
|
||||
import { getLinkToShowPage } from '@/object-metadata/utils/getLinkToShowPage';
|
||||
import { useRecordChipData } from '@/object-record/hooks/useRecordChipData';
|
||||
import { recordIndexOpenRecordInState } from '@/object-record/record-index/states/recordIndexOpenRecordInState';
|
||||
import { ObjectRecord } from '@/object-record/types/ObjectRecord';
|
||||
import { ViewOpenRecordInType } from '@/views/types/ViewOpenRecordInType';
|
||||
import { MouseEvent } from 'react';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
|
||||
export type RecordChipProps = {
|
||||
objectNameSingular: string;
|
||||
@ -23,8 +27,18 @@ export const RecordChip = ({
|
||||
record,
|
||||
});
|
||||
|
||||
const { openRecordInCommandMenu } = useCommandMenu();
|
||||
|
||||
const recordIndexOpenRecordIn = useRecoilValue(recordIndexOpenRecordInState);
|
||||
|
||||
const handleClick = (e: MouseEvent<Element>) => {
|
||||
e.stopPropagation();
|
||||
if (recordIndexOpenRecordIn === ViewOpenRecordInType.SIDE_PANEL) {
|
||||
openRecordInCommandMenu({
|
||||
recordId: record.id,
|
||||
objectNameSingular,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
@ -36,7 +50,11 @@ export const RecordChip = ({
|
||||
className={className}
|
||||
variant={variant}
|
||||
onClick={handleClick}
|
||||
to={getLinkToShowPage(objectNameSingular, record)}
|
||||
to={
|
||||
recordIndexOpenRecordIn === ViewOpenRecordInType.RECORD_PAGE
|
||||
? getLinkToShowPage(objectNameSingular, record)
|
||||
: undefined
|
||||
}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
@ -6,6 +6,7 @@ import { ObjectOptionsDropdownRecordGroupFieldsContent } from '@/object-record/o
|
||||
import { ObjectOptionsDropdownRecordGroupsContent } from '@/object-record/object-options-dropdown/components/ObjectOptionsDropdownRecordGroupsContent';
|
||||
import { ObjectOptionsDropdownRecordGroupSortContent } from '@/object-record/object-options-dropdown/components/ObjectOptionsDropdownRecordGroupSortContent';
|
||||
import { ObjectOptionsDropdownViewSettingsContent } from '@/object-record/object-options-dropdown/components/ObjectOptionsDropdownViewSettingsContent';
|
||||
import { ObjectOptionsDropdownViewSettingsOpenInContent } from '@/object-record/object-options-dropdown/components/ObjectOptionsDropdownViewSettingsOpenInContent';
|
||||
import { useOptionsDropdown } from '@/object-record/object-options-dropdown/hooks/useOptionsDropdown';
|
||||
|
||||
export const ObjectOptionsDropdownContent = () => {
|
||||
@ -14,6 +15,8 @@ export const ObjectOptionsDropdownContent = () => {
|
||||
switch (currentContentId) {
|
||||
case 'viewSettings':
|
||||
return <ObjectOptionsDropdownViewSettingsContent />;
|
||||
case 'viewSettingsOpenIn':
|
||||
return <ObjectOptionsDropdownViewSettingsOpenInContent />;
|
||||
case 'fields':
|
||||
return <ObjectOptionsDropdownFieldsContent />;
|
||||
case 'hiddenFields':
|
||||
|
||||
@ -31,7 +31,6 @@ import { DropdownMenuSeparator } from '@/ui/layout/dropdown/components/DropdownM
|
||||
import { useScopedHotkeys } from '@/ui/utilities/hotkey/hooks/useScopedHotkeys';
|
||||
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
|
||||
import { useGetCurrentView } from '@/views/hooks/useGetCurrentView';
|
||||
import { ViewType } from '@/views/types/ViewType';
|
||||
import { isDefined } from 'twenty-shared';
|
||||
|
||||
export const ObjectOptionsDropdownMenuContent = () => {
|
||||
@ -104,20 +103,17 @@ export const ObjectOptionsDropdownMenuContent = () => {
|
||||
<DropdownMenuHeader StartIcon={CurrentViewIcon ?? IconList}>
|
||||
{currentView?.name}
|
||||
</DropdownMenuHeader>
|
||||
{/** TODO: Should be removed when view settings contains more options */}
|
||||
{viewType === ViewType.Kanban && (
|
||||
<>
|
||||
<DropdownMenuItemsContainer scrollable={false}>
|
||||
<MenuItem
|
||||
onClick={() => onContentChange('viewSettings')}
|
||||
LeftIcon={IconLayout}
|
||||
text="View settings"
|
||||
hasSubMenu
|
||||
/>
|
||||
</DropdownMenuItemsContainer>
|
||||
<DropdownMenuSeparator />
|
||||
</>
|
||||
)}
|
||||
|
||||
<DropdownMenuItemsContainer scrollable={false}>
|
||||
<MenuItem
|
||||
onClick={() => onContentChange('viewSettings')}
|
||||
LeftIcon={IconLayout}
|
||||
text="View settings"
|
||||
hasSubMenu
|
||||
/>
|
||||
</DropdownMenuItemsContainer>
|
||||
<DropdownMenuSeparator />
|
||||
|
||||
<DropdownMenuItemsContainer scrollable={false}>
|
||||
<MenuItem
|
||||
onClick={() => onContentChange('fields')}
|
||||
|
||||
@ -1,21 +1,32 @@
|
||||
import {
|
||||
IconBaselineDensitySmall,
|
||||
IconChevronLeft,
|
||||
IconLayoutNavbar,
|
||||
IconLayoutSidebarRight,
|
||||
MenuItem,
|
||||
MenuItemToggle,
|
||||
} from 'twenty-ui';
|
||||
|
||||
import { useObjectOptionsForBoard } from '@/object-record/object-options-dropdown/hooks/useObjectOptionsForBoard';
|
||||
import { useOptionsDropdown } from '@/object-record/object-options-dropdown/hooks/useOptionsDropdown';
|
||||
import { recordIndexOpenRecordInState } from '@/object-record/record-index/states/recordIndexOpenRecordInState';
|
||||
import { DropdownMenuHeader } from '@/ui/layout/dropdown/components/DropdownMenuHeader';
|
||||
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
|
||||
import { useGetCurrentView } from '@/views/hooks/useGetCurrentView';
|
||||
import { ViewOpenRecordInType } from '@/views/types/ViewOpenRecordInType';
|
||||
import { ViewType } from '@/views/types/ViewType';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
|
||||
export const ObjectOptionsDropdownViewSettingsContent = () => {
|
||||
const { currentViewWithCombinedFiltersAndSorts } = useGetCurrentView();
|
||||
|
||||
const { recordIndexId, objectMetadataItem, viewType, resetContent } =
|
||||
useOptionsDropdown();
|
||||
const {
|
||||
recordIndexId,
|
||||
objectMetadataItem,
|
||||
viewType,
|
||||
resetContent,
|
||||
onContentChange,
|
||||
} = useOptionsDropdown();
|
||||
|
||||
const { isCompactModeActive, setAndPersistIsCompactModeActive } =
|
||||
useObjectOptionsForBoard({
|
||||
@ -24,12 +35,29 @@ export const ObjectOptionsDropdownViewSettingsContent = () => {
|
||||
viewBarId: recordIndexId,
|
||||
});
|
||||
|
||||
const recordIndexOpenRecordIn = useRecoilValue(recordIndexOpenRecordInState);
|
||||
|
||||
return (
|
||||
<>
|
||||
<DropdownMenuHeader StartIcon={IconChevronLeft} onClick={resetContent}>
|
||||
View settings
|
||||
</DropdownMenuHeader>
|
||||
<DropdownMenuItemsContainer>
|
||||
<MenuItem
|
||||
onClick={() => onContentChange('viewSettingsOpenIn')}
|
||||
LeftIcon={
|
||||
recordIndexOpenRecordIn === ViewOpenRecordInType.SIDE_PANEL
|
||||
? IconLayoutSidebarRight
|
||||
: IconLayoutNavbar
|
||||
}
|
||||
text="Open in"
|
||||
contextualText={
|
||||
recordIndexOpenRecordIn === ViewOpenRecordInType.SIDE_PANEL
|
||||
? 'Side Panel'
|
||||
: 'Record Page'
|
||||
}
|
||||
hasSubMenu
|
||||
/>
|
||||
{viewType === ViewType.Kanban && (
|
||||
<MenuItemToggle
|
||||
LeftIcon={IconBaselineDensitySmall}
|
||||
|
||||
@ -0,0 +1,60 @@
|
||||
import {
|
||||
IconChevronLeft,
|
||||
IconLayoutNavbar,
|
||||
IconLayoutSidebarRight,
|
||||
MenuItemSelect,
|
||||
} from 'twenty-ui';
|
||||
|
||||
import { useObjectOptions } from '@/object-record/object-options-dropdown/hooks/useObjectOptions';
|
||||
import { useOptionsDropdown } from '@/object-record/object-options-dropdown/hooks/useOptionsDropdown';
|
||||
import { recordIndexOpenRecordInState } from '@/object-record/record-index/states/recordIndexOpenRecordInState';
|
||||
import { DropdownMenuHeader } from '@/ui/layout/dropdown/components/DropdownMenuHeader';
|
||||
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
|
||||
import { useGetCurrentView } from '@/views/hooks/useGetCurrentView';
|
||||
import { ViewOpenRecordInType } from '@/views/types/ViewOpenRecordInType';
|
||||
import { t } from '@lingui/core/macro';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
|
||||
export const ObjectOptionsDropdownViewSettingsOpenInContent = () => {
|
||||
const { onContentChange } = useOptionsDropdown();
|
||||
const recordIndexOpenRecordIn = useRecoilValue(recordIndexOpenRecordInState);
|
||||
const { currentViewWithCombinedFiltersAndSorts } = useGetCurrentView();
|
||||
const { setAndPersistOpenRecordIn } = useObjectOptions();
|
||||
|
||||
return (
|
||||
<>
|
||||
<DropdownMenuHeader
|
||||
StartIcon={IconChevronLeft}
|
||||
onClick={() => onContentChange('viewSettings')}
|
||||
>
|
||||
{t`Open in`}
|
||||
</DropdownMenuHeader>
|
||||
<DropdownMenuItemsContainer>
|
||||
<MenuItemSelect
|
||||
LeftIcon={IconLayoutSidebarRight}
|
||||
text="Side Panel"
|
||||
selected={recordIndexOpenRecordIn === ViewOpenRecordInType.SIDE_PANEL}
|
||||
onClick={() =>
|
||||
setAndPersistOpenRecordIn(
|
||||
ViewOpenRecordInType.SIDE_PANEL,
|
||||
currentViewWithCombinedFiltersAndSorts,
|
||||
)
|
||||
}
|
||||
/>
|
||||
<MenuItemSelect
|
||||
LeftIcon={IconLayoutNavbar}
|
||||
text="Record Page"
|
||||
selected={
|
||||
recordIndexOpenRecordIn === ViewOpenRecordInType.RECORD_PAGE
|
||||
}
|
||||
onClick={() =>
|
||||
setAndPersistOpenRecordIn(
|
||||
ViewOpenRecordInType.RECORD_PAGE,
|
||||
currentViewWithCombinedFiltersAndSorts,
|
||||
)
|
||||
}
|
||||
/>
|
||||
</DropdownMenuItemsContainer>
|
||||
</>
|
||||
);
|
||||
};
|
||||
@ -0,0 +1,29 @@
|
||||
import { recordIndexOpenRecordInState } from '@/object-record/record-index/states/recordIndexOpenRecordInState';
|
||||
import { useUpdateCurrentView } from '@/views/hooks/useUpdateCurrentView';
|
||||
import { GraphQLView } from '@/views/types/GraphQLView';
|
||||
import { ViewOpenRecordInType } from '@/views/types/ViewOpenRecordInType';
|
||||
import { useCallback } from 'react';
|
||||
import { useSetRecoilState } from 'recoil';
|
||||
|
||||
export const useObjectOptions = () => {
|
||||
const setRecordIndexOpenRecordIn = useSetRecoilState(
|
||||
recordIndexOpenRecordInState,
|
||||
);
|
||||
|
||||
const { updateCurrentView } = useUpdateCurrentView();
|
||||
|
||||
const setAndPersistOpenRecordIn = useCallback(
|
||||
(openRecordIn: ViewOpenRecordInType, view: GraphQLView | undefined) => {
|
||||
if (!view) return;
|
||||
setRecordIndexOpenRecordIn(openRecordIn);
|
||||
updateCurrentView({
|
||||
openRecordIn,
|
||||
});
|
||||
},
|
||||
[setRecordIndexOpenRecordIn, updateCurrentView],
|
||||
);
|
||||
|
||||
return {
|
||||
setAndPersistOpenRecordIn,
|
||||
};
|
||||
};
|
||||
@ -1,5 +1,6 @@
|
||||
export type ObjectOptionsContentId =
|
||||
| 'viewSettings'
|
||||
| 'viewSettingsOpenIn'
|
||||
| 'fields'
|
||||
| 'hiddenFields'
|
||||
| 'recordGroups'
|
||||
|
||||
@ -1,42 +1,45 @@
|
||||
import {
|
||||
AvatarChipVariant,
|
||||
Checkbox,
|
||||
CheckboxVariant,
|
||||
LightIconButton,
|
||||
IconEye,
|
||||
IconEyeOff,
|
||||
} from 'twenty-ui';
|
||||
import { useCommandMenu } from '@/command-menu/hooks/useCommandMenu';
|
||||
import { RecordBoardContext } from '@/object-record/record-board/contexts/RecordBoardContext';
|
||||
import { useRecordBoardSelection } from '@/object-record/record-board/hooks/useRecordBoardSelection';
|
||||
import { RecordBoardCardHeaderContainer } from '@/object-record/record-board/record-board-card/components/RecordBoardCardHeaderContainer';
|
||||
import { RecordInlineCellEditMode } from '@/object-record/record-inline-cell/components/RecordInlineCellEditMode';
|
||||
import styled from '@emotion/styled';
|
||||
import { TextInput } from '@/ui/input/components/TextInput';
|
||||
import { Dispatch, SetStateAction, useContext, useState } from 'react';
|
||||
import { StopPropagationContainer } from '@/object-record/record-board/record-board-card/components/StopPropagationContainer';
|
||||
import { RecordBoardCardContext } from '@/object-record/record-board/record-board-card/contexts/RecordBoardCardContext';
|
||||
import { useAddNewCard } from '@/object-record/record-board/record-board-column/hooks/useAddNewCard';
|
||||
import { RecordBoardScopeInternalContext } from '@/object-record/record-board/scopes/scope-internal-context/RecordBoardScopeInternalContext';
|
||||
import { isRecordBoardCardSelectedComponentFamilyState } from '@/object-record/record-board/states/isRecordBoardCardSelectedComponentFamilyState';
|
||||
import { isRecordBoardCompactModeActiveComponentState } from '@/object-record/record-board/states/isRecordBoardCompactModeActiveComponentState';
|
||||
import { RecordBoardFieldDefinition } from '@/object-record/record-board/types/RecordBoardFieldDefinition';
|
||||
import { FieldMetadata } from '@/object-record/record-field/types/FieldMetadata';
|
||||
import {
|
||||
FieldContext,
|
||||
RecordUpdateHook,
|
||||
RecordUpdateHookParams,
|
||||
} from '@/object-record/record-field/contexts/FieldContext';
|
||||
import { ObjectRecord } from '@/object-record/types/ObjectRecord';
|
||||
import { FieldMetadata } from '@/object-record/record-field/types/FieldMetadata';
|
||||
import { getFieldButtonIcon } from '@/object-record/record-field/utils/getFieldButtonIcon';
|
||||
import { InlineCellHotkeyScope } from '@/object-record/record-inline-cell/types/InlineCellHotkeyScope';
|
||||
import { RecordInlineCell } from '@/object-record/record-inline-cell/components/RecordInlineCell';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import { recordStoreFamilyState } from '@/object-record/record-store/states/recordStoreFamilyState';
|
||||
import { RecordBoardContext } from '@/object-record/record-board/contexts/RecordBoardContext';
|
||||
import { RecordBoardCardContext } from '@/object-record/record-board/record-board-card/contexts/RecordBoardCardContext';
|
||||
import { RecordIdentifierChip } from '@/object-record/record-index/components/RecordIndexRecordChip';
|
||||
import { useRecoilComponentFamilyStateV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentFamilyStateV2';
|
||||
import { isRecordBoardCardSelectedComponentFamilyState } from '@/object-record/record-board/states/isRecordBoardCardSelectedComponentFamilyState';
|
||||
import { useAvailableScopeIdOrThrow } from '@/ui/utilities/recoil-scope/scopes-internal/hooks/useAvailableScopeId';
|
||||
import { RecordBoardScopeInternalContext } from '@/object-record/record-board/scopes/scope-internal-context/RecordBoardScopeInternalContext';
|
||||
import { useRecordBoardSelection } from '@/object-record/record-board/hooks/useRecordBoardSelection';
|
||||
import { useRecordIndexContextOrThrow } from '@/object-record/record-index/contexts/RecordIndexContext';
|
||||
import { recordIndexOpenRecordInState } from '@/object-record/record-index/states/recordIndexOpenRecordInState';
|
||||
import { RecordInlineCell } from '@/object-record/record-inline-cell/components/RecordInlineCell';
|
||||
import { RecordInlineCellEditMode } from '@/object-record/record-inline-cell/components/RecordInlineCellEditMode';
|
||||
import { InlineCellHotkeyScope } from '@/object-record/record-inline-cell/types/InlineCellHotkeyScope';
|
||||
import { recordStoreFamilyState } from '@/object-record/record-store/states/recordStoreFamilyState';
|
||||
import { ObjectRecord } from '@/object-record/types/ObjectRecord';
|
||||
import { TextInput } from '@/ui/input/components/TextInput';
|
||||
import { useAvailableScopeIdOrThrow } from '@/ui/utilities/recoil-scope/scopes-internal/hooks/useAvailableScopeId';
|
||||
import { useRecoilComponentFamilyStateV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentFamilyStateV2';
|
||||
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
|
||||
import { isRecordBoardCompactModeActiveComponentState } from '@/object-record/record-board/states/isRecordBoardCompactModeActiveComponentState';
|
||||
import { StopPropagationContainer } from '@/object-record/record-board/record-board-card/components/StopPropagationContainer';
|
||||
import { ViewOpenRecordInType } from '@/views/types/ViewOpenRecordInType';
|
||||
import styled from '@emotion/styled';
|
||||
import { Dispatch, SetStateAction, useContext, useState } from 'react';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import {
|
||||
AvatarChipVariant,
|
||||
Checkbox,
|
||||
CheckboxVariant,
|
||||
IconEye,
|
||||
IconEyeOff,
|
||||
LightIconButton,
|
||||
} from 'twenty-ui';
|
||||
|
||||
const StyledTextInput = styled(TextInput)`
|
||||
border-radius: ${({ theme }) => theme.border.radius.sm};
|
||||
@ -116,6 +119,10 @@ export const RecordBoardCardHeader = ({
|
||||
return [updateEntity, { loading: false }];
|
||||
};
|
||||
|
||||
const recordIndexOpenRecordIn = useRecoilValue(recordIndexOpenRecordInState);
|
||||
|
||||
const { openRecordInCommandMenu } = useCommandMenu();
|
||||
|
||||
return (
|
||||
<RecordBoardCardHeaderContainer showCompactView={showCompactView}>
|
||||
<StopPropagationContainer>
|
||||
@ -178,7 +185,21 @@ export const RecordBoardCardHeader = ({
|
||||
record={record as ObjectRecord}
|
||||
variant={AvatarChipVariant.Transparent}
|
||||
maxWidth={150}
|
||||
to={indexIdentifierUrl(recordId)}
|
||||
onClick={
|
||||
recordIndexOpenRecordIn === ViewOpenRecordInType.SIDE_PANEL
|
||||
? () => {
|
||||
openRecordInCommandMenu({
|
||||
recordId,
|
||||
objectNameSingular: objectMetadataItem.nameSingular,
|
||||
});
|
||||
}
|
||||
: undefined
|
||||
}
|
||||
to={
|
||||
recordIndexOpenRecordIn === ViewOpenRecordInType.RECORD_PAGE
|
||||
? indexIdentifierUrl(recordId)
|
||||
: undefined
|
||||
}
|
||||
/>
|
||||
)}
|
||||
</StopPropagationContainer>
|
||||
|
||||
@ -1,6 +1,10 @@
|
||||
import { useCommandMenu } from '@/command-menu/hooks/useCommandMenu';
|
||||
import { RecordChip } from '@/object-record/components/RecordChip';
|
||||
import { useChipFieldDisplay } from '@/object-record/record-field/meta-types/hooks/useChipFieldDisplay';
|
||||
import { RecordIdentifierChip } from '@/object-record/record-index/components/RecordIndexRecordChip';
|
||||
import { recordIndexOpenRecordInState } from '@/object-record/record-index/states/recordIndexOpenRecordInState';
|
||||
import { ViewOpenRecordInType } from '@/views/types/ViewOpenRecordInType';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import { ChipSize } from 'twenty-ui';
|
||||
|
||||
export const ChipFieldDisplay = () => {
|
||||
@ -11,6 +15,10 @@ export const ChipFieldDisplay = () => {
|
||||
labelIdentifierLink,
|
||||
} = useChipFieldDisplay();
|
||||
|
||||
const recordIndexOpenRecordIn = useRecoilValue(recordIndexOpenRecordInState);
|
||||
|
||||
const { openRecordInCommandMenu } = useCommandMenu();
|
||||
|
||||
if (!recordValue) {
|
||||
return null;
|
||||
}
|
||||
@ -20,7 +28,21 @@ export const ChipFieldDisplay = () => {
|
||||
objectNameSingular={objectNameSingular}
|
||||
record={recordValue}
|
||||
size={ChipSize.Small}
|
||||
to={labelIdentifierLink}
|
||||
to={
|
||||
recordIndexOpenRecordIn === ViewOpenRecordInType.RECORD_PAGE
|
||||
? labelIdentifierLink
|
||||
: undefined
|
||||
}
|
||||
onClick={
|
||||
recordIndexOpenRecordIn === ViewOpenRecordInType.SIDE_PANEL
|
||||
? () => {
|
||||
openRecordInCommandMenu({
|
||||
recordId: recordValue.id,
|
||||
objectNameSingular,
|
||||
});
|
||||
}
|
||||
: undefined
|
||||
}
|
||||
/>
|
||||
) : (
|
||||
<RecordChip objectNameSingular={objectNameSingular} record={recordValue} />
|
||||
|
||||
@ -11,6 +11,7 @@ export type RecordIdentifierChipProps = {
|
||||
size?: ChipSize;
|
||||
to?: string;
|
||||
maxWidth?: number;
|
||||
onClick?: () => void;
|
||||
};
|
||||
|
||||
export const RecordIdentifierChip = ({
|
||||
@ -18,6 +19,7 @@ export const RecordIdentifierChip = ({
|
||||
record,
|
||||
variant,
|
||||
size,
|
||||
onClick,
|
||||
to,
|
||||
maxWidth,
|
||||
}: RecordIdentifierChipProps) => {
|
||||
@ -40,6 +42,7 @@ export const RecordIdentifierChip = ({
|
||||
avatarType={recordChipData.avatarType}
|
||||
avatarUrl={recordChipData.avatarUrl ?? ''}
|
||||
to={to}
|
||||
onClick={onClick}
|
||||
variant={variant}
|
||||
LeftIcon={LeftIcon}
|
||||
LeftIconColor={LeftIconColor}
|
||||
|
||||
@ -10,6 +10,7 @@ import { recordIndexFiltersState } from '@/object-record/record-index/states/rec
|
||||
import { recordIndexIsCompactModeActiveState } from '@/object-record/record-index/states/recordIndexIsCompactModeActiveState';
|
||||
import { recordIndexKanbanAggregateOperationState } from '@/object-record/record-index/states/recordIndexKanbanAggregateOperationState';
|
||||
import { recordIndexKanbanFieldMetadataIdState } from '@/object-record/record-index/states/recordIndexKanbanFieldMetadataIdState';
|
||||
import { recordIndexOpenRecordInState } from '@/object-record/record-index/states/recordIndexOpenRecordInState';
|
||||
import { recordIndexSortsState } from '@/object-record/record-index/states/recordIndexSortsState';
|
||||
import { recordIndexViewFilterGroupsState } from '@/object-record/record-index/states/recordIndexViewFilterGroupsState';
|
||||
import { recordIndexViewTypeState } from '@/object-record/record-index/states/recordIndexViewTypeState';
|
||||
@ -48,6 +49,9 @@ export const useLoadRecordIndexStates = () => {
|
||||
recordIndexIsCompactModeActiveState,
|
||||
);
|
||||
const setRecordIndexViewType = useSetRecoilState(recordIndexViewTypeState);
|
||||
const setRecordIndexOpenRecordIn = useSetRecoilState(
|
||||
recordIndexOpenRecordInState,
|
||||
);
|
||||
const setRecordIndexViewKanbanFieldMetadataIdState = useSetRecoilState(
|
||||
recordIndexKanbanFieldMetadataIdState,
|
||||
);
|
||||
@ -242,6 +246,7 @@ export const useLoadRecordIndexStates = () => {
|
||||
mapViewSortsToSorts(view.viewSorts, sortDefinitions),
|
||||
);
|
||||
setRecordIndexViewType(view.type);
|
||||
setRecordIndexOpenRecordIn(view.openRecordIn);
|
||||
setRecordIndexViewKanbanFieldMetadataIdState(
|
||||
view.kanbanFieldMetadataId,
|
||||
);
|
||||
@ -272,6 +277,7 @@ export const useLoadRecordIndexStates = () => {
|
||||
setRecordIndexViewKanbanAggregateOperationState,
|
||||
setRecordIndexViewKanbanFieldMetadataIdState,
|
||||
setRecordIndexViewType,
|
||||
setRecordIndexOpenRecordIn,
|
||||
],
|
||||
);
|
||||
|
||||
|
||||
@ -0,0 +1,8 @@
|
||||
import { createState } from '@ui/utilities/state/utils/createState';
|
||||
|
||||
import { ViewOpenRecordInType } from '@/views/types/ViewOpenRecordInType';
|
||||
|
||||
export const recordIndexOpenRecordInState = createState<ViewOpenRecordInType>({
|
||||
key: 'recordIndexOpenRecordInState',
|
||||
defaultValue: ViewOpenRecordInType.SIDE_PANEL,
|
||||
});
|
||||
@ -1,18 +1,19 @@
|
||||
import { useCommandMenu } from '@/command-menu/hooks/useCommandMenu';
|
||||
import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
|
||||
import { useCreateOneRecord } from '@/object-record/hooks/useCreateOneRecord';
|
||||
import { recordIndexOpenRecordInState } from '@/object-record/record-index/states/recordIndexOpenRecordInState';
|
||||
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';
|
||||
import { recordTablePendingRecordIdComponentState } from '@/object-record/record-table/states/recordTablePendingRecordIdComponentState';
|
||||
import { useRecordTitleCell } from '@/object-record/record-title-cell/hooks/useRecordTitleCell';
|
||||
import { getDropdownFocusIdForRecordField } from '@/object-record/utils/getDropdownFocusIdForRecordField';
|
||||
import { shouldRedirectToShowPageOnCreation } from '@/object-record/utils/shouldRedirectToShowPageOnCreation';
|
||||
import { AppPath } from '@/types/AppPath';
|
||||
import { useSetActiveDropdownFocusIdAndMemorizePrevious } from '@/ui/layout/dropdown/hooks/useSetFocusedDropdownIdAndMemorizePrevious';
|
||||
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 { ViewOpenRecordInType } from '@/views/types/ViewOpenRecordInType';
|
||||
import { useIsFeatureEnabled } from '@/workspace/hooks/useIsFeatureEnabled';
|
||||
import { useRecoilCallback } from 'recoil';
|
||||
import { isDefined } from 'twenty-shared';
|
||||
@ -62,62 +63,71 @@ export const useCreateNewTableRecord = ({
|
||||
|
||||
const { openRecordTitleCell } = useRecordTitleCell();
|
||||
|
||||
const createNewTableRecord = async () => {
|
||||
const recordId = v4();
|
||||
const createNewTableRecord = useRecoilCallback(
|
||||
({ snapshot }) =>
|
||||
async () => {
|
||||
const recordId = v4();
|
||||
|
||||
if (isCommandMenuV2Enabled) {
|
||||
// TODO: Generalize this behaviour, there will be a view setting to specify
|
||||
// if the new record should be displayed in the side panel or on the record page
|
||||
if (shouldRedirectToShowPageOnCreation(objectMetadataItem.nameSingular)) {
|
||||
await createOneRecord({
|
||||
id: recordId,
|
||||
name: 'Untitled',
|
||||
});
|
||||
if (isCommandMenuV2Enabled) {
|
||||
const recordIndexOpenRecordIn = snapshot
|
||||
.getLoadable(recordIndexOpenRecordInState)
|
||||
.getValue();
|
||||
|
||||
navigate(AppPath.RecordShowPage, {
|
||||
objectNameSingular: objectMetadataItem.nameSingular,
|
||||
objectRecordId: recordId,
|
||||
});
|
||||
await createOneRecord({ id: recordId });
|
||||
|
||||
// TODO: we should open the record title cell here but because
|
||||
// we are redirecting to the record show page, the hotkey scope will
|
||||
// be overridden by the hotkey scope on mount. We need to deprecate
|
||||
// the useHotkeyScopeOnMount hook.
|
||||
if (recordIndexOpenRecordIn === ViewOpenRecordInType.SIDE_PANEL) {
|
||||
openRecordInCommandMenu({
|
||||
recordId,
|
||||
objectNameSingular: objectMetadataItem.nameSingular,
|
||||
isNewRecord: true,
|
||||
});
|
||||
|
||||
return;
|
||||
}
|
||||
openRecordTitleCell({
|
||||
recordId,
|
||||
fieldMetadataId:
|
||||
objectMetadataItem.labelIdentifierFieldMetadataId,
|
||||
});
|
||||
} else {
|
||||
navigate(AppPath.RecordShowPage, {
|
||||
objectNameSingular: objectMetadataItem.nameSingular,
|
||||
objectRecordId: recordId,
|
||||
});
|
||||
}
|
||||
|
||||
await createOneRecord({ id: recordId });
|
||||
return;
|
||||
}
|
||||
|
||||
openRecordInCommandMenu({
|
||||
recordId,
|
||||
objectNameSingular: objectMetadataItem.nameSingular,
|
||||
isNewRecord: true,
|
||||
});
|
||||
|
||||
openRecordTitleCell({
|
||||
recordId,
|
||||
fieldMetadataId: objectMetadataItem.labelIdentifierFieldMetadataId,
|
||||
});
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
setPendingRecordId(recordId);
|
||||
setSelectedTableCellEditMode(-1, 0);
|
||||
setHotkeyScope(DEFAULT_CELL_SCOPE.scope, DEFAULT_CELL_SCOPE.customScopes);
|
||||
|
||||
if (isDefined(objectMetadataItem.labelIdentifierFieldMetadataId)) {
|
||||
setActiveDropdownFocusIdAndMemorizePrevious(
|
||||
getDropdownFocusIdForRecordField(
|
||||
recordId,
|
||||
objectMetadataItem.labelIdentifierFieldMetadataId,
|
||||
'table-cell',
|
||||
),
|
||||
);
|
||||
}
|
||||
};
|
||||
setPendingRecordId(recordId);
|
||||
setSelectedTableCellEditMode(-1, 0);
|
||||
setHotkeyScope(
|
||||
DEFAULT_CELL_SCOPE.scope,
|
||||
DEFAULT_CELL_SCOPE.customScopes,
|
||||
);
|
||||
|
||||
if (isDefined(objectMetadataItem.labelIdentifierFieldMetadataId)) {
|
||||
setActiveDropdownFocusIdAndMemorizePrevious(
|
||||
getDropdownFocusIdForRecordField(
|
||||
recordId,
|
||||
objectMetadataItem.labelIdentifierFieldMetadataId,
|
||||
'table-cell',
|
||||
),
|
||||
);
|
||||
}
|
||||
},
|
||||
[
|
||||
createOneRecord,
|
||||
isCommandMenuV2Enabled,
|
||||
navigate,
|
||||
objectMetadataItem.labelIdentifierFieldMetadataId,
|
||||
objectMetadataItem.nameSingular,
|
||||
openRecordInCommandMenu,
|
||||
openRecordTitleCell,
|
||||
setActiveDropdownFocusIdAndMemorizePrevious,
|
||||
setHotkeyScope,
|
||||
setPendingRecordId,
|
||||
setSelectedTableCellEditMode,
|
||||
],
|
||||
);
|
||||
const createNewTableRecordInGroup = useRecoilCallback(
|
||||
({ set }) =>
|
||||
(recordGroupId: string) => {
|
||||
|
||||
@ -20,11 +20,14 @@ import { useClickOutsideListener } from '@/ui/utilities/pointer-event/hooks/useC
|
||||
import { getSnapshotValue } from '@/ui/utilities/state/utils/getSnapshotValue';
|
||||
import { isDefined } from 'twenty-shared';
|
||||
|
||||
import { useCommandMenu } from '@/command-menu/hooks/useCommandMenu';
|
||||
import { useRecordIndexContextOrThrow } from '@/object-record/record-index/contexts/RecordIndexContext';
|
||||
import { recordIndexOpenRecordInState } from '@/object-record/record-index/states/recordIndexOpenRecordInState';
|
||||
import { RECORD_TABLE_CLICK_OUTSIDE_LISTENER_ID } from '@/object-record/record-table/constants/RecordTableClickOutsideListenerId';
|
||||
import { getDropdownFocusIdForRecordField } from '@/object-record/utils/getDropdownFocusIdForRecordField';
|
||||
import { useSetActiveDropdownFocusIdAndMemorizePrevious } from '@/ui/layout/dropdown/hooks/useSetFocusedDropdownIdAndMemorizePrevious';
|
||||
import { useClickOustideListenerStates } from '@/ui/utilities/pointer-event/hooks/useClickOustideListenerStates';
|
||||
import { ViewOpenRecordInType } from '@/views/types/ViewOpenRecordInType';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import { IconList } from 'twenty-ui';
|
||||
import { TableHotkeyScope } from '../../types/TableHotkeyScope';
|
||||
@ -75,6 +78,8 @@ export const useOpenRecordTableCellV2 = (tableScopeId: string) => {
|
||||
const { setActiveDropdownFocusIdAndMemorizePrevious } =
|
||||
useSetActiveDropdownFocusIdAndMemorizePrevious();
|
||||
|
||||
const { openRecordInCommandMenu } = useCommandMenu();
|
||||
|
||||
const openTableCell = useRecoilCallback(
|
||||
({ snapshot, set }) =>
|
||||
({
|
||||
@ -115,7 +120,20 @@ export const useOpenRecordTableCellV2 = (tableScopeId: string) => {
|
||||
) {
|
||||
leaveTableFocus();
|
||||
|
||||
navigate(indexIdentifierUrl(recordId));
|
||||
const openRecordIn = snapshot
|
||||
.getLoadable(recordIndexOpenRecordInState)
|
||||
.getValue();
|
||||
|
||||
if (openRecordIn === ViewOpenRecordInType.RECORD_PAGE) {
|
||||
navigate(indexIdentifierUrl(recordId));
|
||||
}
|
||||
|
||||
if (openRecordIn === ViewOpenRecordInType.SIDE_PANEL) {
|
||||
openRecordInCommandMenu({
|
||||
recordId,
|
||||
objectNameSingular,
|
||||
});
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
@ -170,14 +188,15 @@ export const useOpenRecordTableCellV2 = (tableScopeId: string) => {
|
||||
moveEditModeToTableCellPosition,
|
||||
initDraftValue,
|
||||
toggleClickOutsideListener,
|
||||
setActiveDropdownFocusIdAndMemorizePrevious,
|
||||
leaveTableFocus,
|
||||
navigate,
|
||||
indexIdentifierUrl,
|
||||
openRecordInCommandMenu,
|
||||
setViewableRecordId,
|
||||
setViewableRecordNameSingular,
|
||||
openRightDrawer,
|
||||
setHotkeyScope,
|
||||
setActiveDropdownFocusIdAndMemorizePrevious,
|
||||
],
|
||||
);
|
||||
|
||||
|
||||
@ -1,11 +0,0 @@
|
||||
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
|
||||
|
||||
export const shouldRedirectToShowPageOnCreation = (
|
||||
objectNameSingular: string,
|
||||
) => {
|
||||
if (objectNameSingular === CoreObjectNameSingular.Workflow) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
@ -10,6 +10,7 @@ export const findAllViewsOperationSignatureFactory: RecordGqlOperationSignatureF
|
||||
createdAt: true,
|
||||
updatedAt: true,
|
||||
isCompact: true,
|
||||
openRecordIn: true,
|
||||
objectMetadataId: true,
|
||||
position: true,
|
||||
type: true,
|
||||
|
||||
@ -10,6 +10,7 @@ import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/
|
||||
import { View } from '@/views/types/View';
|
||||
import { ViewFilter } from '@/views/types/ViewFilter';
|
||||
import { ViewFilterOperand } from '@/views/types/ViewFilterOperand';
|
||||
import { ViewOpenRecordInType } from '@/views/types/ViewOpenRecordInType';
|
||||
import { ViewType } from '@/views/types/ViewType';
|
||||
import { act } from 'react';
|
||||
import { isDefined } from 'twenty-shared';
|
||||
@ -51,6 +52,7 @@ describe('useApplyCurrentViewFiltersToCurrentRecordFilters', () => {
|
||||
type: ViewType.Table,
|
||||
key: null,
|
||||
isCompact: false,
|
||||
openRecordIn: ViewOpenRecordInType.SIDE_PANEL,
|
||||
viewFields: [],
|
||||
viewGroups: [],
|
||||
viewSorts: [],
|
||||
|
||||
@ -4,6 +4,7 @@ import { ViewFilter } from '@/views/types/ViewFilter';
|
||||
import { ViewFilterGroup } from '@/views/types/ViewFilterGroup';
|
||||
import { ViewGroup } from '@/views/types/ViewGroup';
|
||||
import { ViewKey } from '@/views/types/ViewKey';
|
||||
import { ViewOpenRecordInType } from '@/views/types/ViewOpenRecordInType';
|
||||
import { ViewSort } from '@/views/types/ViewSort';
|
||||
import { ViewType } from '@/views/types/ViewType';
|
||||
|
||||
@ -20,6 +21,7 @@ export type GraphQLView = {
|
||||
kanbanAggregateOperationFieldMetadataId?: string | null;
|
||||
objectMetadataId: string;
|
||||
isCompact: boolean;
|
||||
openRecordIn: ViewOpenRecordInType;
|
||||
viewFields: ViewField[];
|
||||
viewFilters: ViewFilter[];
|
||||
viewFilterGroups?: ViewFilterGroup[];
|
||||
|
||||
@ -4,6 +4,7 @@ import { ViewFilter } from '@/views/types/ViewFilter';
|
||||
import { ViewFilterGroup } from '@/views/types/ViewFilterGroup';
|
||||
import { ViewGroup } from '@/views/types/ViewGroup';
|
||||
import { ViewKey } from '@/views/types/ViewKey';
|
||||
import { ViewOpenRecordInType } from '@/views/types/ViewOpenRecordInType';
|
||||
import { ViewSort } from '@/views/types/ViewSort';
|
||||
import { ViewType } from '@/views/types/ViewType';
|
||||
|
||||
@ -27,5 +28,6 @@ export type View = {
|
||||
kanbanAggregateOperationFieldMetadataId: string | null;
|
||||
position: number;
|
||||
icon: string;
|
||||
openRecordIn: ViewOpenRecordInType;
|
||||
__typename: 'View';
|
||||
};
|
||||
|
||||
@ -0,0 +1,4 @@
|
||||
export enum ViewOpenRecordInType {
|
||||
SIDE_PANEL = 'SIDE_PANEL',
|
||||
RECORD_PAGE = 'RECORD_PAGE',
|
||||
}
|
||||
@ -1,3 +1,4 @@
|
||||
import { ActionMenuContext } from '@/action-menu/contexts/ActionMenuContext';
|
||||
import { useCommandMenu } from '@/command-menu/hooks/useCommandMenu';
|
||||
import { commandMenuNavigationStackState } from '@/command-menu/states/commandMenuNavigationStackState';
|
||||
import { useRightDrawer } from '@/ui/layout/right-drawer/hooks/useRightDrawer';
|
||||
@ -16,7 +17,7 @@ import { getWorkflowNodeIconKey } from '@/workflow/workflow-diagram/utils/getWor
|
||||
import { isCreateStepNode } from '@/workflow/workflow-diagram/utils/isCreateStepNode';
|
||||
import { useLingui } from '@lingui/react/macro';
|
||||
import { OnSelectionChangeParams, useOnSelectionChange } from '@xyflow/react';
|
||||
import { useCallback } from 'react';
|
||||
import { useCallback, useContext } from 'react';
|
||||
import { useSetRecoilState } from 'recoil';
|
||||
import { isDefined } from 'twenty-shared';
|
||||
import { IconBolt, useIcons } from 'twenty-ui';
|
||||
@ -37,12 +38,16 @@ export const WorkflowDiagramCanvasEditableEffect = () => {
|
||||
commandMenuNavigationStackState,
|
||||
);
|
||||
|
||||
const { isInRightDrawer } = useContext(ActionMenuContext);
|
||||
|
||||
const handleSelectionChange = useCallback(
|
||||
({ nodes }: OnSelectionChangeParams) => {
|
||||
const selectedNode = nodes[0] as WorkflowDiagramNode;
|
||||
const isClosingStep = isDefined(selectedNode) === false;
|
||||
|
||||
setCommandMenuNavigationStack([]);
|
||||
if (!isInRightDrawer) {
|
||||
setCommandMenuNavigationStack([]);
|
||||
}
|
||||
|
||||
if (isClosingStep) {
|
||||
closeRightDrawer();
|
||||
@ -76,11 +81,12 @@ export const WorkflowDiagramCanvasEditableEffect = () => {
|
||||
});
|
||||
},
|
||||
[
|
||||
setCommandMenuNavigationStack,
|
||||
isInRightDrawer,
|
||||
setWorkflowSelectedNode,
|
||||
setHotkeyScope,
|
||||
openRightDrawer,
|
||||
getIcon,
|
||||
setCommandMenuNavigationStack,
|
||||
closeRightDrawer,
|
||||
closeCommandMenu,
|
||||
t,
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
import { AGGREGATE_OPERATIONS } from '@/object-record/record-table/constants/AggregateOperations';
|
||||
import { View } from '@/views/types/View';
|
||||
import { ViewKey } from '@/views/types/ViewKey';
|
||||
import { ViewOpenRecordInType } from '@/views/types/ViewOpenRecordInType';
|
||||
import { ViewType } from '@/views/types/ViewType';
|
||||
import { generatedMockObjectMetadataItems } from '~/testing/mock-data/generatedMockObjectMetadataItems';
|
||||
|
||||
@ -29,6 +30,7 @@ export const mockedViewsData: View[] = [
|
||||
kanbanAggregateOperationFieldMetadataId: '',
|
||||
position: 0,
|
||||
isCompact: false,
|
||||
openRecordIn: ViewOpenRecordInType.SIDE_PANEL,
|
||||
viewFilterGroups: [],
|
||||
viewGroups: [],
|
||||
viewFields: [],
|
||||
@ -48,6 +50,7 @@ export const mockedViewsData: View[] = [
|
||||
kanbanAggregateOperationFieldMetadataId: '',
|
||||
position: 0,
|
||||
isCompact: false,
|
||||
openRecordIn: ViewOpenRecordInType.SIDE_PANEL,
|
||||
viewFilterGroups: [],
|
||||
viewGroups: [],
|
||||
viewFields: [],
|
||||
@ -67,6 +70,7 @@ export const mockedViewsData: View[] = [
|
||||
kanbanAggregateOperationFieldMetadataId: '',
|
||||
position: 0,
|
||||
isCompact: false,
|
||||
openRecordIn: ViewOpenRecordInType.SIDE_PANEL,
|
||||
viewFilterGroups: [],
|
||||
viewGroups: [],
|
||||
viewFields: [],
|
||||
@ -86,6 +90,7 @@ export const mockedViewsData: View[] = [
|
||||
kanbanAggregateOperationFieldMetadataId: '',
|
||||
position: 0,
|
||||
isCompact: false,
|
||||
openRecordIn: ViewOpenRecordInType.SIDE_PANEL,
|
||||
viewFilterGroups: [],
|
||||
viewGroups: [],
|
||||
viewFields: [],
|
||||
|
||||
Reference in New Issue
Block a user