Disable reset to default when custom tab or widget (#19814)
## Context "Reset to default" action is rejected by the backend for custom entities because there is no "default" concept for them ## Implementation Grey out the Reset to default action on record page-layout tabs and widgets when the entity either has no applicationId yet (unsaved draft — previously slipped through the existing check), or belongs to the workspace custom application. <img width="926" height="370" alt="Screenshot 2026-04-17 at 18 50 31" src="https://github.com/user-attachments/assets/c7c163f4-17a6-4b69-a66d-90f9085d27a2" />
This commit is contained in:
+19
-6
@@ -13,6 +13,7 @@ import { SelectableListItem } from '@/ui/layout/selectable-list/components/Selec
|
||||
import { useLingui } from '@lingui/react/macro';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import {
|
||||
AppTooltip,
|
||||
IconChevronLeft,
|
||||
IconChevronRight,
|
||||
IconEyeX,
|
||||
@@ -22,6 +23,8 @@ import {
|
||||
} from 'twenty-ui/display';
|
||||
|
||||
const RESET_TAB_TO_DEFAULT_MODAL_ID = 'reset-canvas-tab-to-default-modal';
|
||||
const RESET_TAB_TO_DEFAULT_MENU_ITEM_ID =
|
||||
'reset-canvas-tab-to-default-menu-item';
|
||||
|
||||
type CanvasTabSettingsContentProps = {
|
||||
pageLayoutId: string;
|
||||
@@ -29,7 +32,7 @@ type CanvasTabSettingsContentProps = {
|
||||
canSetAsPinned: boolean;
|
||||
canMoveLeft: boolean;
|
||||
canMoveRight: boolean;
|
||||
canShowResetToDefault: boolean;
|
||||
isResetToDefaultDisabled: boolean;
|
||||
canDelete: boolean;
|
||||
onMoveLeft: () => void;
|
||||
onMoveRight: () => void;
|
||||
@@ -44,7 +47,7 @@ export const CanvasTabSettingsContent = ({
|
||||
canSetAsPinned,
|
||||
canMoveLeft,
|
||||
canMoveRight,
|
||||
canShowResetToDefault,
|
||||
isResetToDefaultDisabled,
|
||||
canDelete,
|
||||
onMoveLeft,
|
||||
onMoveRight,
|
||||
@@ -60,6 +63,9 @@ export const CanvasTabSettingsContent = ({
|
||||
);
|
||||
|
||||
const handleResetToDefault = () => {
|
||||
if (isResetToDefaultDisabled) {
|
||||
return;
|
||||
}
|
||||
openModal(RESET_TAB_TO_DEFAULT_MODAL_ID);
|
||||
};
|
||||
|
||||
@@ -70,9 +76,7 @@ export const CanvasTabSettingsContent = ({
|
||||
...(isDefined(canvasWidget)
|
||||
? [TAB_SETTINGS_SELECTABLE_ITEM_IDS.VISIBILITY_RESTRICTION]
|
||||
: []),
|
||||
...(canShowResetToDefault
|
||||
? [TAB_SETTINGS_SELECTABLE_ITEM_IDS.RESET_TO_DEFAULT]
|
||||
: []),
|
||||
TAB_SETTINGS_SELECTABLE_ITEM_IDS.RESET_TO_DEFAULT,
|
||||
...(canDelete ? [TAB_SETTINGS_SELECTABLE_ITEM_IDS.DELETE] : []),
|
||||
];
|
||||
|
||||
@@ -149,7 +153,7 @@ export const CanvasTabSettingsContent = ({
|
||||
/>
|
||||
</SelectableListItem>
|
||||
)}
|
||||
{canShowResetToDefault && (
|
||||
<div id={RESET_TAB_TO_DEFAULT_MENU_ITEM_ID}>
|
||||
<SelectableListItem
|
||||
itemId={TAB_SETTINGS_SELECTABLE_ITEM_IDS.RESET_TO_DEFAULT}
|
||||
onEnter={handleResetToDefault}
|
||||
@@ -159,8 +163,17 @@ export const CanvasTabSettingsContent = ({
|
||||
Icon={IconRefreshDot}
|
||||
label={t`Reset to default`}
|
||||
onClick={handleResetToDefault}
|
||||
disabled={isResetToDefaultDisabled}
|
||||
/>
|
||||
</SelectableListItem>
|
||||
</div>
|
||||
{isResetToDefaultDisabled && (
|
||||
<AppTooltip
|
||||
anchorSelect={`#${RESET_TAB_TO_DEFAULT_MENU_ITEM_ID}`}
|
||||
content={t`No default configuration available for this tab`}
|
||||
noArrow
|
||||
place="bottom"
|
||||
/>
|
||||
)}
|
||||
{canDelete && (
|
||||
<SelectableListItem
|
||||
|
||||
+19
-6
@@ -7,6 +7,7 @@ import { useModal } from '@/ui/layout/modal/hooks/useModal';
|
||||
import { SelectableListItem } from '@/ui/layout/selectable-list/components/SelectableListItem';
|
||||
import { useLingui } from '@lingui/react/macro';
|
||||
import {
|
||||
AppTooltip,
|
||||
IconChevronLeft,
|
||||
IconChevronRight,
|
||||
IconCopyPlus,
|
||||
@@ -16,12 +17,14 @@ import {
|
||||
} from 'twenty-ui/display';
|
||||
|
||||
const RESET_TAB_TO_DEFAULT_MODAL_ID = 'reset-regular-tab-to-default-modal';
|
||||
const RESET_TAB_TO_DEFAULT_MENU_ITEM_ID =
|
||||
'reset-regular-tab-to-default-menu-item';
|
||||
|
||||
type RegularTabSettingsContentProps = {
|
||||
canSetAsPinned: boolean;
|
||||
canMoveLeft: boolean;
|
||||
canMoveRight: boolean;
|
||||
canShowResetToDefault: boolean;
|
||||
isResetToDefaultDisabled: boolean;
|
||||
canDelete: boolean;
|
||||
onMoveLeft: () => void;
|
||||
onMoveRight: () => void;
|
||||
@@ -35,7 +38,7 @@ export const RegularTabSettingsContent = ({
|
||||
canSetAsPinned,
|
||||
canMoveLeft,
|
||||
canMoveRight,
|
||||
canShowResetToDefault,
|
||||
isResetToDefaultDisabled,
|
||||
canDelete,
|
||||
onMoveLeft,
|
||||
onMoveRight,
|
||||
@@ -48,6 +51,9 @@ export const RegularTabSettingsContent = ({
|
||||
const { openModal } = useModal();
|
||||
|
||||
const handleResetToDefault = () => {
|
||||
if (isResetToDefaultDisabled) {
|
||||
return;
|
||||
}
|
||||
openModal(RESET_TAB_TO_DEFAULT_MODAL_ID);
|
||||
};
|
||||
|
||||
@@ -56,9 +62,7 @@ export const RegularTabSettingsContent = ({
|
||||
...(canMoveRight ? [TAB_SETTINGS_SELECTABLE_ITEM_IDS.MOVE_RIGHT] : []),
|
||||
...(canSetAsPinned ? [TAB_SETTINGS_SELECTABLE_ITEM_IDS.SET_AS_PINNED] : []),
|
||||
TAB_SETTINGS_SELECTABLE_ITEM_IDS.DUPLICATE,
|
||||
...(canShowResetToDefault
|
||||
? [TAB_SETTINGS_SELECTABLE_ITEM_IDS.RESET_TO_DEFAULT]
|
||||
: []),
|
||||
TAB_SETTINGS_SELECTABLE_ITEM_IDS.RESET_TO_DEFAULT,
|
||||
...(canDelete ? [TAB_SETTINGS_SELECTABLE_ITEM_IDS.DELETE] : []),
|
||||
];
|
||||
|
||||
@@ -116,7 +120,7 @@ export const RegularTabSettingsContent = ({
|
||||
onClick={onDuplicate}
|
||||
/>
|
||||
</SelectableListItem>
|
||||
{canShowResetToDefault && (
|
||||
<div id={RESET_TAB_TO_DEFAULT_MENU_ITEM_ID}>
|
||||
<SelectableListItem
|
||||
itemId={TAB_SETTINGS_SELECTABLE_ITEM_IDS.RESET_TO_DEFAULT}
|
||||
onEnter={handleResetToDefault}
|
||||
@@ -126,8 +130,17 @@ export const RegularTabSettingsContent = ({
|
||||
Icon={IconRefreshDot}
|
||||
label={t`Reset to default`}
|
||||
onClick={handleResetToDefault}
|
||||
disabled={isResetToDefaultDisabled}
|
||||
/>
|
||||
</SelectableListItem>
|
||||
</div>
|
||||
{isResetToDefaultDisabled && (
|
||||
<AppTooltip
|
||||
anchorSelect={`#${RESET_TAB_TO_DEFAULT_MENU_ITEM_ID}`}
|
||||
content={t`No default configuration available for this tab`}
|
||||
noArrow
|
||||
place="bottom"
|
||||
/>
|
||||
)}
|
||||
{canDelete && (
|
||||
<SelectableListItem
|
||||
|
||||
+1
@@ -276,6 +276,7 @@ export const SidePanelPageLayoutRecordPageWidgetTypeSelect = () => {
|
||||
const newWidget: PageLayoutWidget = {
|
||||
__typename: 'PageLayoutWidget',
|
||||
id: widgetId,
|
||||
applicationId: '',
|
||||
isActive: true,
|
||||
pageLayoutTabId: tabId,
|
||||
title: frontComponent.name,
|
||||
|
||||
+6
-7
@@ -14,6 +14,7 @@ import { RegularTabSettingsContent } from '@/side-panel/pages/page-layout/compon
|
||||
import { useAtomComponentState } from '@/ui/utilities/state/jotai/hooks/useAtomComponentState';
|
||||
import { useAtomComponentStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomComponentStateValue';
|
||||
import { useAtomStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomStateValue';
|
||||
import { isNonEmptyString } from '@sniptt/guards';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import {
|
||||
@@ -85,11 +86,9 @@ export const SidePanelPageLayoutTabSettingsContent = ({
|
||||
const canSetAsPinned =
|
||||
isRecordPage && !isAlreadyPinned && tabsSorted.length > 1;
|
||||
|
||||
const isCustomTab =
|
||||
isDefined(currentWorkspace?.workspaceCustomApplication) &&
|
||||
tab.applicationId === currentWorkspace.workspaceCustomApplication.id;
|
||||
|
||||
const canShowResetToDefault = !isCustomTab;
|
||||
const isResetToDefaultDisabled =
|
||||
!isNonEmptyString(tab.applicationId) ||
|
||||
tab.applicationId === currentWorkspace?.workspaceCustomApplication?.id;
|
||||
|
||||
const handleDelete = () => {
|
||||
deleteTab(tab.id);
|
||||
@@ -111,7 +110,7 @@ export const SidePanelPageLayoutTabSettingsContent = ({
|
||||
canSetAsPinned={canSetAsPinned}
|
||||
canMoveLeft={canMoveLeft}
|
||||
canMoveRight={canMoveRight}
|
||||
canShowResetToDefault={canShowResetToDefault}
|
||||
isResetToDefaultDisabled={isResetToDefaultDisabled}
|
||||
canDelete={canDelete}
|
||||
onMoveLeft={() => moveLeft(tab.id)}
|
||||
onMoveRight={() => moveRight(tab.id)}
|
||||
@@ -127,7 +126,7 @@ export const SidePanelPageLayoutTabSettingsContent = ({
|
||||
canSetAsPinned={canSetAsPinned}
|
||||
canMoveLeft={canMoveLeft}
|
||||
canMoveRight={canMoveRight}
|
||||
canShowResetToDefault={canShowResetToDefault}
|
||||
isResetToDefaultDisabled={isResetToDefaultDisabled}
|
||||
canDelete={canDelete}
|
||||
onMoveLeft={() => moveLeft(tab.id)}
|
||||
onMoveRight={() => moveRight(tab.id)}
|
||||
|
||||
+37
-10
@@ -1,3 +1,4 @@
|
||||
import { currentWorkspaceState } from '@/auth/states/currentWorkspaceState';
|
||||
import { CommandMenuItem } from '@/command-menu/components/CommandMenuItem';
|
||||
import { CommandMenuItemDropdown } from '@/command-menu/components/CommandMenuItemDropdown';
|
||||
import { useDeletePageLayoutWidget } from '@/page-layout/hooks/useDeletePageLayoutWidget';
|
||||
@@ -14,10 +15,13 @@ import { ConfirmationModal } from '@/ui/layout/modal/components/ConfirmationModa
|
||||
import { useModal } from '@/ui/layout/modal/hooks/useModal';
|
||||
import { SelectableListItem } from '@/ui/layout/selectable-list/components/SelectableListItem';
|
||||
import { useAtomComponentStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomComponentStateValue';
|
||||
import { useAtomStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomStateValue';
|
||||
import { useLingui } from '@lingui/react/macro';
|
||||
import { isNonEmptyString } from '@sniptt/guards';
|
||||
import { SidePanelPages } from 'twenty-shared/types';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import {
|
||||
AppTooltip,
|
||||
IconEyeX,
|
||||
IconRefreshDot,
|
||||
IconSwitchHorizontal,
|
||||
@@ -25,6 +29,8 @@ import {
|
||||
} from 'twenty-ui/display';
|
||||
|
||||
const RESET_WIDGET_TO_DEFAULT_MODAL_ID = 'reset-widget-to-default-modal';
|
||||
const RESET_WIDGET_TO_DEFAULT_MENU_ITEM_ID =
|
||||
'reset-widget-to-default-menu-item';
|
||||
|
||||
type WidgetSettingsManageSectionProps = {
|
||||
pageLayoutId: string;
|
||||
@@ -42,6 +48,8 @@ export const WidgetSettingsManageSection = ({
|
||||
pageLayoutId,
|
||||
);
|
||||
|
||||
const currentWorkspace = useAtomStateValue(currentWorkspaceState);
|
||||
|
||||
const { deletePageLayoutWidget } = useDeletePageLayoutWidget(pageLayoutId);
|
||||
|
||||
const { resetPageLayoutWidgetToDefault } =
|
||||
@@ -59,7 +67,15 @@ export const WidgetSettingsManageSection = ({
|
||||
return null;
|
||||
}
|
||||
|
||||
const isResetToDefaultDisabled =
|
||||
!isNonEmptyString(widgetInEditMode?.applicationId) ||
|
||||
widgetInEditMode.applicationId ===
|
||||
currentWorkspace?.workspaceCustomApplication?.id;
|
||||
|
||||
const handleResetToDefault = () => {
|
||||
if (isResetToDefaultDisabled) {
|
||||
return;
|
||||
}
|
||||
openModal(RESET_WIDGET_TO_DEFAULT_MODAL_ID);
|
||||
};
|
||||
|
||||
@@ -100,17 +116,28 @@ export const WidgetSettingsManageSection = ({
|
||||
contextualTextPosition="right"
|
||||
/>
|
||||
</SelectableListItem>
|
||||
<SelectableListItem
|
||||
itemId={WIDGET_SETTINGS_SELECTABLE_ITEM_IDS.RESET_TO_DEFAULT}
|
||||
onEnter={handleResetToDefault}
|
||||
>
|
||||
<CommandMenuItem
|
||||
id={WIDGET_SETTINGS_SELECTABLE_ITEM_IDS.RESET_TO_DEFAULT}
|
||||
Icon={IconRefreshDot}
|
||||
label={t`Reset to default`}
|
||||
onClick={handleResetToDefault}
|
||||
<div id={RESET_WIDGET_TO_DEFAULT_MENU_ITEM_ID}>
|
||||
<SelectableListItem
|
||||
itemId={WIDGET_SETTINGS_SELECTABLE_ITEM_IDS.RESET_TO_DEFAULT}
|
||||
onEnter={handleResetToDefault}
|
||||
>
|
||||
<CommandMenuItem
|
||||
id={WIDGET_SETTINGS_SELECTABLE_ITEM_IDS.RESET_TO_DEFAULT}
|
||||
Icon={IconRefreshDot}
|
||||
label={t`Reset to default`}
|
||||
onClick={handleResetToDefault}
|
||||
disabled={isResetToDefaultDisabled}
|
||||
/>
|
||||
</SelectableListItem>
|
||||
</div>
|
||||
{isResetToDefaultDisabled && (
|
||||
<AppTooltip
|
||||
anchorSelect={`#${RESET_WIDGET_TO_DEFAULT_MENU_ITEM_ID}`}
|
||||
content={t`No default configuration available for this widget`}
|
||||
noArrow
|
||||
place="bottom"
|
||||
/>
|
||||
</SelectableListItem>
|
||||
)}
|
||||
<SelectableListItem
|
||||
itemId={WIDGET_SETTINGS_SELECTABLE_ITEM_IDS.REPLACE_WIDGET}
|
||||
onEnter={handleReplaceWidget}
|
||||
|
||||
Reference in New Issue
Block a user