Refactor record layouts for backend-driven configuration (#15021)
## Refactor: Prepare frontend record layouts for backend-driven configuration This PR simplifies and prepares the frontend record layout system for eventual migration to backend-driven layouts, aligning the architecture with the existing PageLayout system used for dashboards. ### Key Changes **Architecture Improvements:** - Created unified LayoutRenderingContext that works for both record pages (with targetRecord) and dashboards (standalone) - Replaced prop drilling with context-based data flow - cards access targetRecord and isInRightDrawer via context hooks - Introduced useTargetRecord() helper hook that provides type-safe access to the current record - Created generic CardRenderer component that handles configuration injection and context guards uniformly **Configuration System:** - Converted all tab icons from React components to JSON-serializable strings (e.g., Icon: IconCheckbox → icon: 'IconCheckbox') - Added configuration field to cards, matching the widget configuration pattern on the backend - Created CardConfiguration types system similar to WidgetConfiguration on the backend - Moved widget-specific props (like showDuplicatesSection) into configuration objects **Code Organization:** - Extracted visibility evaluation logic into reusable evaluateTabVisibility() utility - Organized layouts into dedicated files (one per object: base-record-layout.ts, company-record-layout.ts, etc.) - Renamed components to match their purpose: Notes → NotesCard, Attachments → FilesCard, etc. - Consolidated card rendering from registry object to direct getCardComponent() function **API Alignment:** - Made ifNoReadPermissionObject an explicit part of TabVisibilityConfig (follows if* naming convention) - Removed redundant targetObjectNameSingular from tab-level (now derived from visibility config) - Card API now mirrors Widget API (both use type, configuration, accessed via context)
This commit is contained in:
+3
-1
@@ -1,6 +1,7 @@
|
||||
import { useFormContext } from 'react-hook-form';
|
||||
|
||||
import { useObjectMetadataItems } from '@/object-metadata/hooks/useObjectMetadataItems';
|
||||
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
|
||||
import { SettingsDataModelPreviewFormCard } from '@/settings/data-model/components/SettingsDataModelPreviewFormCard';
|
||||
import { RELATION_TYPES } from '@/settings/data-model/constants/RelationTypes';
|
||||
import {
|
||||
@@ -82,7 +83,8 @@ export const SettingsDataModelFieldRelationSettingsFormCard = ({
|
||||
}}
|
||||
shrink
|
||||
objectNameSingulars={[
|
||||
relationObjectMetadataItem?.nameSingular ?? 'company',
|
||||
relationObjectMetadataItem?.nameSingular ??
|
||||
CoreObjectNameSingular.Company,
|
||||
]}
|
||||
fieldPreviewTargetObjectNameSingular={objectNameSingular}
|
||||
pluralizeLabel={oppositeRelationType === RelationType.ONE_TO_MANY}
|
||||
|
||||
+2
-1
@@ -2,6 +2,7 @@ import { useTheme } from '@emotion/react';
|
||||
import styled from '@emotion/styled';
|
||||
|
||||
import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
|
||||
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
|
||||
import { type FieldMetadataItem } from '@/object-metadata/types/FieldMetadataItem';
|
||||
import { type FieldMetadataItemRelation } from '@/object-metadata/types/FieldMetadataItemRelation';
|
||||
import { FieldDisplay } from '@/object-record/record-field/ui/components/FieldDisplay';
|
||||
@@ -75,7 +76,7 @@ export const SettingsDataModelRelationFieldPreview = ({
|
||||
|
||||
const metadata = {
|
||||
fieldName,
|
||||
objectMetadataNameSingular: 'company',
|
||||
objectMetadataNameSingular: CoreObjectNameSingular.Company,
|
||||
relationObjectMetadataNameSingular: relationTargetObjectNameSingular,
|
||||
options: [],
|
||||
settings: fieldMetadataItem.settings,
|
||||
|
||||
Reference in New Issue
Block a user