Fix 19026 deactivated relation unassignable (#19296)

PR to fix the bug #19026 

This PR will ensure that if an object has some relation deactivated, the
relation will not be visible in the side panel tab and will not be
assignable in the deactivated relation.

## Notes deactivated in People
<img width="1068" height="1106" alt="image"
src="https://github.com/user-attachments/assets/e8c2dbf3-5391-4dbc-8e40-79fcc44e8158"
/>

## Notes not visible in the side panel of people
<img width="2390" height="892" alt="image"
src="https://github.com/user-attachments/assets/308a78aa-2c6d-4d3d-b67c-b49795839aae"
/>

---------

Co-authored-by: Thomas Trompette <thomas.trompette@sfr.fr>
This commit is contained in:
Lakshay Manchanda
2026-05-18 08:25:51 -07:00
committed by GitHub
parent 4ba9c0ca0b
commit 7c252ff233
2 changed files with 59 additions and 3 deletions
@@ -1,10 +1,12 @@
import { metadataStoreState } from '@/metadata-store/states/metadataStoreState';
import { type FlatObjectMetadataItem } from '@/metadata-store/types/FlatObjectMetadataItem';
import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
import { PageLayoutLeftPanel } from '@/page-layout/components/PageLayoutLeftPanel';
import { PageLayoutTabList } from '@/page-layout/components/PageLayoutTabList';
import { PageLayoutTabListEffect } from '@/page-layout/components/PageLayoutTabListEffect';
import { DEFAULT_RECORD_PAGE_LAYOUT_ID } from '@/page-layout/constants/DefaultRecordPageLayoutId';
import { PAGE_LAYOUT_LEFT_PANEL_CONTAINER_WIDTH } from '@/page-layout/constants/PageLayoutLeftPanelContainerWidth';
import { WIDGET_TYPE_TO_RELATION_FIELD_NAME } from '@/page-layout/constants/WidgetTypeToRelationFieldName';
import { useCurrentPageLayoutOrThrow } from '@/page-layout/hooks/useCurrentPageLayoutOrThrow';
import { useIsPageLayoutInEditMode } from '@/page-layout/hooks/useIsPageLayoutInEditMode';
import { usePageLayoutAddTabStrategy } from '@/page-layout/hooks/usePageLayoutAddTabStrategy';
@@ -17,13 +19,17 @@ import { getTabsWithVisibleWidgets } from '@/page-layout/utils/getTabsWithVisibl
import { shouldEnableTabEditingFeatures } from '@/page-layout/utils/shouldEnableTabEditingFeatures';
import { sortTabsByPosition } from '@/page-layout/utils/sortTabsByPosition';
import { useLayoutRenderingContext } from '@/ui/layout/contexts/LayoutRenderingContext';
import { useTargetRecord } from '@/ui/layout/contexts/useTargetRecord';
import { activeTabIdComponentState } from '@/ui/layout/tab-list/states/activeTabIdComponentState';
import { ScrollWrapper } from '@/ui/utilities/scroll/components/ScrollWrapper';
import { useAtomComponentStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomComponentStateValue';
import { useAtomFamilyStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomFamilyStateValue';
import { styled } from '@linaria/react';
import { useMemo } from 'react';
import { FieldMetadataType } from 'twenty-shared/types';
import { isDefined } from 'twenty-shared/utils';
import { useIsMobile } from 'twenty-ui/utilities';
const StyledContainer = styled.div<{ hasPinnedTab: boolean }>`
display: grid;
grid-template-columns: ${({ hasPinnedTab }) =>
@@ -69,6 +75,28 @@ export const PageLayoutTabsRenderer = () => {
currentPageLayout.id,
);
const targetRecord = useTargetRecord();
const { objectMetadataItem } = useObjectMetadataItem({
objectNameSingular: targetRecord.targetObjectNameSingular,
});
const inactiveRelationFieldNames = useMemo(() => {
if (!isDefined(objectMetadataItem)) {
return new Set<string>();
}
return new Set(
objectMetadataItem.fields
.filter(
(field) =>
!field.isActive &&
(field.type === FieldMetadataType.RELATION ||
field.type === FieldMetadataType.MORPH_RELATION),
)
.map((field) => field.name),
);
}, [objectMetadataItem]);
const isMobile = useIsMobile();
const metadataStore = useAtomFamilyStateValue(
@@ -114,6 +142,22 @@ export const PageLayoutTabsRenderer = () => {
const sortedTabs = sortTabsByPosition(tabsToRenderInTabList);
const sortedActiveTabs = useMemo(
() =>
sortedTabs.filter((tab) => {
const widgetTypes = tab.widgets.map((widget) => widget.type);
return !widgetTypes.some((widgetType) => {
const relationFieldName =
WIDGET_TYPE_TO_RELATION_FIELD_NAME[widgetType];
return (
isDefined(relationFieldName) &&
inactiveRelationFieldNames.has(relationFieldName)
);
});
}),
[sortedTabs, inactiveRelationFieldNames],
);
const activeTabExistsInCurrentPageLayout = currentPageLayout.tabs.some(
(tab) => tab.id === activeTabId,
);
@@ -126,16 +170,16 @@ export const PageLayoutTabsRenderer = () => {
<StyledTabsAndDashboardContainer>
<PageLayoutTabListEffect
tabs={sortedTabs}
tabs={sortedActiveTabs}
componentInstanceId={tabListInstanceId}
defaultTabToFocusOnMobileAndSidePanelId={
currentPageLayout.defaultTabToFocusOnMobileAndSidePanelId ??
undefined
}
/>
{(sortedTabs.length > 1 || isPageLayoutInEditMode) && (
{(sortedActiveTabs.length > 1 || isPageLayoutInEditMode) && (
<PageLayoutTabList
tabs={sortedTabs}
tabs={sortedActiveTabs}
behaveAsLinks={!isInSidePanel && !isPageLayoutInEditMode}
isInSidePanel={isInSidePanel}
componentInstanceId={tabListInstanceId}
@@ -0,0 +1,12 @@
import { WidgetType } from '~/generated-metadata/graphql';
export const WIDGET_TYPE_TO_RELATION_FIELD_NAME: Partial<
Record<WidgetType, string>
> = {
[WidgetType.TASKS]: 'taskTargets',
[WidgetType.NOTES]: 'noteTargets',
[WidgetType.FILES]: 'attachments',
[WidgetType.TIMELINE]: 'timelineActivities',
[WidgetType.EMAILS]: 'messageParticipants',
[WidgetType.CALENDAR]: 'calendarEventParticipants',
};