Extract DashboardCard from ShowPageSubContainer (#15073)

This PR determines the minimal setup required to render dashboards
without errors while extracting them from the record show page. The
ultimate goal is to create a `PageLayoutRenderer` component that takes
any page layout configuration and correctly renders dashboards or record
pages.

Currently, the `DashboardCard` component renders itself the
`PageLayoutRenderer` component. The next step is to reverse the flow of
control and make `PageLayoutRenderer` take a configuration and decide
whether it should render a dashboard or something else.

This PR takes into consideration two comments left by Charles on [the
first PR scaffolding the
refactor](https://github.com/twentyhq/twenty/pull/15021): renaming
`targetRecord` to `targetRecordIdentifier` and adding tests to
`evaluateTabVisibility()`.

## Demo to assert this PR doesn't break everything


https://github.com/user-attachments/assets/b5b99cf3-08fa-43d3-8da1-79018bc63641
This commit is contained in:
Baptiste Devessier
2025-10-14 16:09:44 +02:00
committed by GitHub
parent 7d747c9876
commit 23de047787
16 changed files with 498 additions and 110 deletions
@@ -25,7 +25,7 @@ const meta: Meta<typeof CalendarEventsCard> = {
(Story) => (
<LayoutRenderingProvider
value={{
targetRecord: {
targetRecordIdentifier: {
id: '1',
targetObjectNameSingular: CoreObjectNameSingular.Company,
},
@@ -22,7 +22,7 @@ const meta: Meta<typeof TimelineCard> = {
return (
<LayoutRenderingProvider
value={{
targetRecord: {
targetRecordIdentifier: {
id: '1',
targetObjectNameSingular: CoreObjectNameSingular.Company,
},
@@ -6,8 +6,7 @@ import { CommandMenuPageComponentInstanceContext } from '@/command-menu/states/c
import { ContextStoreComponentInstanceContext } from '@/context-store/states/contexts/ContextStoreComponentInstanceContext';
import { INFORMATION_BANNER_HEIGHT } from '@/information-banner/constants/InformationBannerHeight';
import { RecordComponentInstanceContextsWrapper } from '@/object-record/components/RecordComponentInstanceContextsWrapper';
import { RecordShowContainer } from '@/object-record/record-show/components/RecordShowContainer';
import { RecordShowEffect } from '@/object-record/record-show/components/RecordShowEffect';
import { PageLayoutDispatcher } from '@/object-record/record-show/components/PageLayoutDispatcher';
import { useRecordShowPage } from '@/object-record/record-show/hooks/useRecordShowPage';
import { recordStoreFamilySelector } from '@/object-record/record-store/states/selectors/recordStoreFamilySelector';
import { useIsMobile } from '@/ui/utilities/responsive/hooks/useIsMobile';
@@ -89,15 +88,12 @@ export const CommandMenuRecordPage = () => {
recordId: objectRecordId,
}}
>
<RecordShowEffect
objectNameSingular={objectNameSingular}
recordId={objectRecordId}
/>
<RecordShowContainer
objectNameSingular={objectNameSingular}
objectRecordId={objectRecordId}
loading={false}
isInRightDrawer={true}
<PageLayoutDispatcher
targetRecordIdentifier={{
id: objectRecordId,
targetObjectNameSingular: objectNameSingular,
}}
isInRightDrawer
/>
</TimelineActivityContext.Provider>
</StyledRightDrawerRecord>
@@ -27,7 +27,7 @@ export const MergePreviewTab = ({
return (
<LayoutRenderingProvider
value={{
targetRecord: {
targetRecordIdentifier: {
id: recordId,
targetObjectNameSingular: objectNameSingular,
},
@@ -18,7 +18,7 @@ export const MergeRecordTab = ({
return (
<LayoutRenderingProvider
value={{
targetRecord: {
targetRecordIdentifier: {
id: recordId,
targetObjectNameSingular: objectNameSingular,
},
@@ -0,0 +1,140 @@
import { RecordShowRightDrawerActionMenu } from '@/action-menu/components/RecordShowRightDrawerActionMenu';
import { RecordShowRightDrawerOpenRecordButton } from '@/action-menu/components/RecordShowRightDrawerOpenRecordButton';
import { DashboardCard } from '@/dashboards/components/DashboardCard';
import { InformationBannerDeletedRecord } from '@/information-banner/components/deleted-record/InformationBannerDeletedRecord';
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
import { RecordShowContainer } from '@/object-record/record-show/components/RecordShowContainer';
import { RecordShowContainerContextStoreTargetedRecordsEffect } from '@/object-record/record-show/components/RecordShowContainerContextStoreTargetedRecordsEffect';
import { RecordShowEffect } from '@/object-record/record-show/components/RecordShowEffect';
import { recordStoreFamilySelector } from '@/object-record/record-store/states/selectors/recordStoreFamilySelector';
import { LayoutRenderingProvider } from '@/ui/layout/contexts/LayoutRenderingContext';
import { type TargetRecordIdentifier } from '@/ui/layout/contexts/TargetRecordIdentifier';
import { RightDrawerFooter } from '@/ui/layout/right-drawer/components/RightDrawerFooter';
import styled from '@emotion/styled';
import { useRecoilValue } from 'recoil';
import { PageLayoutType } from '~/generated/graphql';
const StyledShowPageBannerContainer = styled.div`
z-index: 1;
`;
const StyledShowPageRightContainer = styled.div`
display: flex;
flex-direction: column;
height: 100%;
justify-content: start;
width: 100%;
height: 100%;
overflow: auto;
`;
const StyledContentContainer = styled.div<{ isInRightDrawer: boolean }>`
flex: 1;
overflow-y: auto;
background: ${({ theme }) => theme.background.primary};
padding-bottom: ${({ theme, isInRightDrawer }) =>
isInRightDrawer ? theme.spacing(16) : 0};
`;
export const PageLayoutDispatcher = ({
targetRecordIdentifier,
isInRightDrawer = false,
}: {
targetRecordIdentifier: TargetRecordIdentifier;
isInRightDrawer?: boolean;
}) => {
const recordDeletedAt = useRecoilValue<string | null>(
recordStoreFamilySelector({
recordId: targetRecordIdentifier.id,
fieldName: 'deletedAt',
}),
);
if (
targetRecordIdentifier.targetObjectNameSingular ===
CoreObjectNameSingular.Dashboard
) {
return (
<>
<RecordShowEffect
objectNameSingular={targetRecordIdentifier.targetObjectNameSingular}
recordId={targetRecordIdentifier.id}
/>
<RecordShowContainerContextStoreTargetedRecordsEffect
recordId={targetRecordIdentifier.id}
/>
{recordDeletedAt && (
<StyledShowPageBannerContainer>
<InformationBannerDeletedRecord
recordId={targetRecordIdentifier.id}
objectNameSingular={
targetRecordIdentifier.targetObjectNameSingular
}
/>
</StyledShowPageBannerContainer>
)}
<StyledShowPageRightContainer>
<LayoutRenderingProvider
value={{
targetRecordIdentifier: {
id: targetRecordIdentifier.id,
targetObjectNameSingular:
targetRecordIdentifier.targetObjectNameSingular,
},
layoutType: PageLayoutType.RECORD_PAGE,
isInRightDrawer,
}}
>
<StyledContentContainer isInRightDrawer={isInRightDrawer}>
<DashboardCard />
</StyledContentContainer>
</LayoutRenderingProvider>
{isInRightDrawer && (
<RightDrawerFooter
actions={[
<RecordShowRightDrawerActionMenu />,
<RecordShowRightDrawerOpenRecordButton
objectNameSingular={
targetRecordIdentifier.targetObjectNameSingular
}
recordId={targetRecordIdentifier.id}
/>,
]}
/>
)}
</StyledShowPageRightContainer>
</>
);
}
return (
<>
<RecordShowEffect
objectNameSingular={targetRecordIdentifier.targetObjectNameSingular}
recordId={targetRecordIdentifier.id}
/>
<LayoutRenderingProvider
value={{
targetRecordIdentifier: {
id: targetRecordIdentifier.id,
targetObjectNameSingular:
targetRecordIdentifier.targetObjectNameSingular,
},
layoutType: PageLayoutType.RECORD_PAGE,
isInRightDrawer,
}}
>
<RecordShowContainer
objectNameSingular={targetRecordIdentifier.targetObjectNameSingular}
objectRecordId={targetRecordIdentifier.id}
isInRightDrawer={isInRightDrawer}
/>
</LayoutRenderingProvider>
</>
);
};
@@ -20,15 +20,12 @@ const StyledShowPageBannerContainer = styled.div`
type RecordShowContainerProps = {
objectNameSingular: string;
objectRecordId: string;
loading: boolean;
isInRightDrawer?: boolean;
isNewRightDrawerItemLoading?: boolean;
};
export const RecordShowContainer = ({
objectNameSingular,
objectRecordId,
loading,
isInRightDrawer = false,
}: RecordShowContainerProps) => {
const { objectMetadataItem } = useObjectMetadataItem({
@@ -47,7 +44,6 @@ export const RecordShowContainer = ({
);
const { layout, tabs } = useRecordShowContainerTabs(
loading,
objectNameSingular as CoreObjectNameSingular,
isInRightDrawer,
objectMetadataItem,
@@ -70,12 +66,12 @@ export const RecordShowContainer = ({
<ShowPageSubContainer
tabs={tabs}
layout={layout}
targetableObject={{
targetRecordIdentifier={{
id: objectRecordId,
targetObjectNameSingular: objectMetadataItem?.nameSingular,
}}
isInRightDrawer={isInRightDrawer}
loading={isPrefetchLoading || loading || recordLoading}
loading={isPrefetchLoading || recordLoading}
/>
</ShowPageContainer>
</RightDrawerProvider>
@@ -38,7 +38,6 @@ const OBJECT_SPECIFIC_LAYOUTS: Partial<
};
export const useRecordShowContainerTabs = (
loading: boolean,
targetObjectNameSingular: CoreObjectNameSingular,
isInRightDrawer: boolean,
objectMetadataItem: ObjectMetadataItem,
@@ -101,7 +100,7 @@ export const useRecordShowContainerTabs = (
title,
Icon,
cards,
hide: loading || shouldHide,
hide: shouldHide,
};
})
// When isInRightDrawer === true, we merge first and second tab into first tab
@@ -0,0 +1,271 @@
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
import { type ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
import {
type TabVisibilityContext,
evaluateTabVisibility,
} from '@/object-record/record-show/utils/evaluateTabVisibility';
import { type ObjectPermissions } from 'twenty-shared/types';
import { FeatureFlagKey, FieldMetadataType } from '~/generated/graphql';
const makeObjectMetadataItem = (
overrides: Partial<ObjectMetadataItem> = {},
): ObjectMetadataItem => {
return {
id: '1',
nameSingular: CoreObjectNameSingular.Company,
namePlural: 'companies',
labelSingular: 'Company',
labelPlural: 'Companies',
isActive: true,
isCustom: false,
isLabelSyncedWithName: false,
isRemote: false,
isSearchable: true,
isSystem: false,
isUIReadOnly: false,
createdAt: '',
updatedAt: '',
description: '',
fields: [],
readableFields: [],
updatableFields: [],
labelIdentifierFieldMetadataId: '',
indexMetadatas: [],
icon: '',
shortcut: '',
standardOverrides: undefined,
imageIdentifierFieldMetadataId: undefined,
duplicateCriteria: undefined,
...overrides,
};
};
const makeObjectPermissions = (
overrides: Partial<ObjectPermissions> = {},
): ObjectPermissions => {
return {
canReadObjectRecords: true,
canUpdateObjectRecords: true,
canSoftDeleteObjectRecords: true,
canDestroyObjectRecords: true,
restrictedFields: {},
...overrides,
};
};
describe('evaluateTabVisibility', () => {
const baseContext: TabVisibilityContext = {
isMobile: false,
isInRightDrawer: false,
currentWorkspace: {
featureFlags: [{ key: FeatureFlagKey.IS_AI_ENABLED, value: true }],
},
objectMetadataItems: [makeObjectMetadataItem()],
objectPermissionsByObjectMetadataId: {
'1': { ...makeObjectPermissions(), objectMetadataId: '1' },
},
targetObjectMetadataItem: makeObjectMetadataItem(),
};
const baseConfig = {
ifMobile: false,
ifDesktop: false,
ifInRightDrawer: false,
ifFeaturesDisabled: [],
ifRequiredObjectsInactive: [],
ifRelationsMissing: [],
};
it('returns true if ifMobile and isMobile', () => {
expect(
evaluateTabVisibility(
{ ...baseConfig, ifMobile: true },
{ ...baseContext, isMobile: true },
),
).toBe(true);
});
it('returns true if ifDesktop and not isMobile', () => {
expect(
evaluateTabVisibility(
{ ...baseConfig, ifDesktop: true },
{ ...baseContext, isMobile: false },
),
).toBe(true);
});
it('returns true if ifInRightDrawer and isInRightDrawer', () => {
expect(
evaluateTabVisibility(
{ ...baseConfig, ifInRightDrawer: true },
{ ...baseContext, isInRightDrawer: true },
),
).toBe(true);
});
it('returns true if feature flag is disabled', () => {
expect(
evaluateTabVisibility(
{
...baseConfig,
ifFeaturesDisabled: [FeatureFlagKey.IS_CALENDAR_VIEW_ENABLED],
},
baseContext,
),
).toBe(true);
});
it('returns false if feature flag is enabled', () => {
const ctx = {
...baseContext,
currentWorkspace: {
featureFlags: [
{ key: FeatureFlagKey.IS_AI_ENABLED, value: true },
{ key: FeatureFlagKey.IS_CALENDAR_VIEW_ENABLED, value: true },
],
},
};
expect(
evaluateTabVisibility(
{
...baseConfig,
ifFeaturesDisabled: [FeatureFlagKey.IS_CALENDAR_VIEW_ENABLED],
},
ctx,
),
).toBe(false);
});
it('returns true if required object is inactive', () => {
const ctx = {
...baseContext,
objectMetadataItems: [
...baseContext.objectMetadataItems,
makeObjectMetadataItem({
id: '2',
nameSingular: CoreObjectNameSingular.Person,
isActive: false,
}),
],
};
expect(
evaluateTabVisibility(
{
...baseConfig,
ifRequiredObjectsInactive: [CoreObjectNameSingular.Person],
},
ctx,
),
).toBe(true);
});
it('returns true if relation is missing', () => {
const ctx = {
...baseContext,
targetObjectMetadataItem: makeObjectMetadataItem({ fields: [] }),
};
expect(
evaluateTabVisibility(
{ ...baseConfig, ifRelationsMissing: ['contact'] },
ctx,
),
).toBe(true);
});
it('returns true if no read permission', () => {
const ctx = {
...baseContext,
objectMetadataItems: [
...baseContext.objectMetadataItems,
makeObjectMetadataItem({
id: '2',
nameSingular: CoreObjectNameSingular.Person,
}),
],
objectPermissionsByObjectMetadataId: {
...baseContext.objectPermissionsByObjectMetadataId,
'2': {
...makeObjectPermissions({ canReadObjectRecords: false }),
objectMetadataId: '2',
},
},
};
expect(
evaluateTabVisibility(
{
...baseConfig,
ifNoReadPermission: true,
ifNoReadPermissionObject: CoreObjectNameSingular.Person,
},
ctx,
),
).toBe(true);
});
it('returns false if no hide conditions are met', () => {
expect(evaluateTabVisibility(baseConfig, baseContext)).toBe(false);
});
it('returns false if required object is active', () => {
expect(
evaluateTabVisibility(
{
...baseConfig,
ifRequiredObjectsInactive: [CoreObjectNameSingular.Company],
},
baseContext,
),
).toBe(false);
});
it('returns false if relation exists and is active', () => {
const ctx = {
...baseContext,
targetObjectMetadataItem: makeObjectMetadataItem({
fields: [
{
name: 'contact',
type: FieldMetadataType.RELATION,
isActive: true,
} as any,
],
}),
};
expect(
evaluateTabVisibility(
{ ...baseConfig, ifRelationsMissing: ['contact'] },
ctx,
),
).toBe(false);
});
it('returns false if read permission exists', () => {
const ctx = {
...baseContext,
objectMetadataItems: [
...baseContext.objectMetadataItems,
makeObjectMetadataItem({
id: '2',
nameSingular: CoreObjectNameSingular.Person,
}),
],
objectPermissionsByObjectMetadataId: {
...baseContext.objectPermissionsByObjectMetadataId,
'2': {
...makeObjectPermissions({ canReadObjectRecords: true }),
objectMetadataId: '2',
},
},
};
expect(
evaluateTabVisibility(
{
...baseConfig,
ifNoReadPermission: true,
ifNoReadPermissionObject: CoreObjectNameSingular.Person,
},
ctx,
),
).toBe(false);
});
});
@@ -4,7 +4,6 @@ import { FilesCard } from '@/activities/files/components/FilesCard';
import { NotesCard } from '@/activities/notes/components/NotesCard';
import { TasksCard } from '@/activities/tasks/components/TasksCard';
import { TimelineCard } from '@/activities/timeline-activities/components/TimelineCard';
import { DashboardCard } from '@/dashboards/components/DashboardCard';
import { FieldsCard } from '@/object-record/record-show/components/FieldsCard';
import {
type CardConfiguration,
@@ -26,9 +25,9 @@ const CardRenderer = <T extends CardConfiguration>({
Component: React.ComponentType<{ configuration?: T }> | React.ComponentType;
configuration?: T;
}) => {
const { targetRecord } = useLayoutRenderingContext();
const { targetRecordIdentifier } = useLayoutRenderingContext();
if (!targetRecord) {
if (!targetRecordIdentifier) {
return null;
}
@@ -87,7 +86,8 @@ export const getCardComponent = <T extends CardType>(
return <CardRenderer Component={WorkflowRunCard} />;
case CardType.DashboardCard:
return <CardRenderer Component={DashboardCard} />;
throw new Error('Dashboard are handled separately currently');
default:
assertUnreachable(cardType);
}
@@ -22,7 +22,7 @@ export const PageLayoutRenderer = ({
return (
<LayoutRenderingProvider
value={{
targetRecord: undefined,
targetRecordIdentifier: undefined,
layoutType: PageLayoutType.DASHBOARD,
isInRightDrawer: false,
}}
@@ -1,15 +1,12 @@
import { type ActivityTargetableObject } from '@/activities/types/ActivityTargetableEntity';
import { type PageLayoutType } from '~/generated/graphql';
import { createRequiredContext } from '~/utils/createRequiredContext';
import { type TargetRecordIdentifier } from './TargetRecordIdentifier';
export type LayoutRenderingContextType = {
// Optional target record - only present for record pages that display data about a specific record
// Undefined for dashboards which are standalone
// Uses ActivityTargetableObject shape for compatibility with existing components
targetRecord?: Pick<
ActivityTargetableObject,
'id' | 'targetObjectNameSingular'
>;
targetRecordIdentifier: TargetRecordIdentifier | undefined;
layoutType: PageLayoutType;
@@ -0,0 +1,6 @@
import { type ActivityTargetableObject } from '@/activities/types/ActivityTargetableEntity';
export type TargetRecordIdentifier = Pick<
ActivityTargetableObject,
'id' | 'targetObjectNameSingular'
>;
@@ -1,13 +1,13 @@
import { useLayoutRenderingContext } from '@/ui/layout/contexts/LayoutRenderingContext';
export const useTargetRecord = () => {
const { targetRecord } = useLayoutRenderingContext();
const { targetRecordIdentifier } = useLayoutRenderingContext();
if (!targetRecord) {
if (!targetRecordIdentifier) {
throw new Error(
'useTargetRecord must be used within a record page context (targetRecord is required)',
'useTargetRecord must be used within a record page context (targetRecordIdentifier is required)',
);
}
return targetRecord;
return targetRecordIdentifier;
};
@@ -1,28 +1,27 @@
import { RecordShowRightDrawerActionMenu } from '@/action-menu/components/RecordShowRightDrawerActionMenu';
import { RecordShowRightDrawerOpenRecordButton } from '@/action-menu/components/RecordShowRightDrawerOpenRecordButton';
import { type ActivityTargetableObject } from '@/activities/types/ActivityTargetableEntity';
import { CommandMenuPageComponentInstanceContext } from '@/command-menu/states/contexts/CommandMenuPageComponentInstanceContext';
import { FieldsCard } from '@/object-record/record-show/components/FieldsCard';
import { SummaryCard } from '@/object-record/record-show/components/SummaryCard';
import { type RecordLayout } from '@/object-record/record-show/types/RecordLayout';
import { getCardComponent } from '@/object-record/record-show/utils/getCardComponent';
import { LayoutRenderingProvider } from '@/ui/layout/contexts/LayoutRenderingContext';
import { RightDrawerFooter } from '@/ui/layout/right-drawer/components/RightDrawerFooter';
import { ShowPageLeftContainer } from '@/ui/layout/show-page/components/ShowPageLeftContainer';
import { getShowPageTabListComponentId } from '@/ui/layout/show-page/utils/getShowPageTabListComponentId';
import { TabList } from '@/ui/layout/tab-list/components/TabList';
import { PageLayoutType } from '~/generated/graphql';
import { type TargetRecordIdentifier } from '@/ui/layout/contexts/TargetRecordIdentifier';
import { activeTabIdComponentState } from '@/ui/layout/tab-list/states/activeTabIdComponentState';
import { TabListComponentInstanceContext } from '@/ui/layout/tab-list/states/contexts/TabListComponentInstanceContext';
import { type SingleTabProps } from '@/ui/layout/tab-list/types/SingleTabProps';
import { useIsMobile } from '@/ui/utilities/responsive/hooks/useIsMobile';
import { useComponentInstanceStateContext } from '@/ui/utilities/state/component-state/hooks/useComponentInstanceStateContext';
import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue';
import { css } from '@emotion/react';
import styled from '@emotion/styled';
import React from 'react';
const StyledShowPageRightContainer = styled.div<{ isMobile: boolean }>`
const StyledShowPageRightContainer = styled.div`
display: flex;
flex-direction: column;
height: 100%;
@@ -35,11 +34,11 @@ const StyledShowPageRightContainer = styled.div<{ isMobile: boolean }>`
const StyledTabListContainer = styled.div<{ shouldDisplay: boolean }>`
${({ shouldDisplay }) =>
!shouldDisplay &&
`
visibility: hidden;
height: 0;
overflow: hidden;
`}
css`
height: 0;
overflow: hidden;
visibility: hidden;
`}
`;
const StyledTabList = styled(TabList)`
@@ -57,10 +56,7 @@ const StyledContentContainer = styled.div<{ isInRightDrawer: boolean }>`
type ShowPageSubContainerProps = {
layout?: RecordLayout;
tabs: SingleTabProps[];
targetableObject: Pick<
ActivityTargetableObject,
'targetObjectNameSingular' | 'id'
>;
targetRecordIdentifier: TargetRecordIdentifier;
isInRightDrawer?: boolean;
loading: boolean;
};
@@ -68,7 +64,7 @@ type ShowPageSubContainerProps = {
export const ShowPageSubContainer = ({
tabs,
layout,
targetableObject,
targetRecordIdentifier,
loading,
isInRightDrawer = false,
}: ShowPageSubContainerProps) => {
@@ -78,7 +74,7 @@ export const ShowPageSubContainer = ({
const tabListComponentId = getShowPageTabListComponentId({
pageId: commandMenuPageComponentInstance?.instanceId,
targetObjectId: targetableObject.id,
targetObjectId: targetRecordIdentifier.id,
});
const activeTabId = useRecoilComponentValue(
activeTabIdComponentState,
@@ -89,8 +85,8 @@ export const ShowPageSubContainer = ({
const summaryCard = (
<SummaryCard
objectNameSingular={targetableObject.targetObjectNameSingular}
objectRecordId={targetableObject.id}
objectNameSingular={targetRecordIdentifier.targetObjectNameSingular}
objectRecordId={targetRecordIdentifier.id}
isInRightDrawer={isInRightDrawer}
/>
);
@@ -118,52 +114,43 @@ export const ShowPageSubContainer = ({
layout && !layout.hideSummaryAndFields && !isMobile && !isInRightDrawer;
return (
<LayoutRenderingProvider
value={{
targetRecord: {
id: targetableObject.id,
targetObjectNameSingular: targetableObject.targetObjectNameSingular,
},
layoutType: PageLayoutType.RECORD_PAGE,
isInRightDrawer,
}}
<TabListComponentInstanceContext.Provider
value={{ instanceId: tabListComponentId }}
>
<TabListComponentInstanceContext.Provider
value={{ instanceId: tabListComponentId }}
>
{displaySummaryAndFields && (
<ShowPageLeftContainer forceMobile={isMobile}>
{summaryCard}
{fieldsCard}
</ShowPageLeftContainer>
{displaySummaryAndFields && (
<ShowPageLeftContainer forceMobile={isMobile}>
{summaryCard}
{fieldsCard}
</ShowPageLeftContainer>
)}
<StyledShowPageRightContainer>
<StyledTabListContainer shouldDisplay={visibleTabs.length > 1}>
<StyledTabList
behaveAsLinks={!isInRightDrawer}
loading={loading}
tabs={tabs}
isInRightDrawer={isInRightDrawer}
componentInstanceId={tabListComponentId}
/>
</StyledTabListContainer>
{(isMobile || isInRightDrawer) && summaryCard}
<StyledContentContainer isInRightDrawer={isInRightDrawer}>
{renderActiveTabContent()}
</StyledContentContainer>
{isInRightDrawer && (
<RightDrawerFooter
actions={[
<RecordShowRightDrawerActionMenu />,
<RecordShowRightDrawerOpenRecordButton
objectNameSingular={
targetRecordIdentifier.targetObjectNameSingular
}
recordId={targetRecordIdentifier.id}
/>,
]}
/>
)}
<StyledShowPageRightContainer isMobile={isMobile}>
<StyledTabListContainer shouldDisplay={visibleTabs.length > 1}>
<StyledTabList
behaveAsLinks={!isInRightDrawer}
loading={loading}
tabs={tabs}
isInRightDrawer={isInRightDrawer}
componentInstanceId={tabListComponentId}
/>
</StyledTabListContainer>
{(isMobile || isInRightDrawer) && summaryCard}
<StyledContentContainer isInRightDrawer={isInRightDrawer}>
{renderActiveTabContent()}
</StyledContentContainer>
{isInRightDrawer && (
<RightDrawerFooter
actions={[
<RecordShowRightDrawerActionMenu />,
<RecordShowRightDrawerOpenRecordButton
objectNameSingular={targetableObject.targetObjectNameSingular}
recordId={targetableObject.id}
/>,
]}
/>
)}
</StyledShowPageRightContainer>
</TabListComponentInstanceContext.Provider>
</LayoutRenderingProvider>
</StyledShowPageRightContainer>
</TabListComponentInstanceContext.Provider>
);
};
@@ -6,15 +6,14 @@ import { TimelineActivityContext } from '@/activities/timeline-activities/contex
import { MAIN_CONTEXT_STORE_INSTANCE_ID } from '@/context-store/constants/MainContextStoreInstanceId';
import { ContextStoreComponentInstanceContext } from '@/context-store/states/contexts/ContextStoreComponentInstanceContext';
import { RecordComponentInstanceContextsWrapper } from '@/object-record/components/RecordComponentInstanceContextsWrapper';
import { RecordShowContainer } from '@/object-record/record-show/components/RecordShowContainer';
import { RecordShowEffect } from '@/object-record/record-show/components/RecordShowEffect';
import { PageLayoutDispatcher } from '@/object-record/record-show/components/PageLayoutDispatcher';
import { useRecordShowPage } from '@/object-record/record-show/hooks/useRecordShowPage';
import { computeRecordShowComponentInstanceId } from '@/object-record/record-show/utils/computeRecordShowComponentInstanceId';
import { PageHeaderToggleCommandMenuButton } from '@/ui/layout/page-header/components/PageHeaderToggleCommandMenuButton';
import { PageBody } from '@/ui/layout/page/components/PageBody';
import { PageContainer } from '@/ui/layout/page/components/PageContainer';
import { RecordShowPageHeader } from '~/pages/object-record/RecordShowPageHeader';
import { RecordShowPageTitle } from '~/pages/object-record/RecordShowPageTitle';
import { useRecordShowPage } from '@/object-record/record-show/hooks/useRecordShowPage';
export const RecordShowPage = () => {
const parameters = useParams<{
@@ -58,14 +57,11 @@ export const RecordShowPage = () => {
recordId: objectRecordId,
}}
>
<RecordShowEffect
objectNameSingular={objectNameSingular}
recordId={objectRecordId}
/>
<RecordShowContainer
objectNameSingular={objectNameSingular}
objectRecordId={objectRecordId}
loading={false}
<PageLayoutDispatcher
targetRecordIdentifier={{
id: objectRecordId,
targetObjectNameSingular: objectNameSingular,
}}
/>
</TimelineActivityContext.Provider>
</PageBody>