Add email thread widget and message thread record page layout (#19351)

## Summary
- Move email thread display from side panel to a dedicated record page
with a new `EMAIL_THREAD` widget type
- Add message thread as a standard object with page layout, subject
field, and backfill command
- Add reply-to-email command menu item for message thread records
- Remove old side panel message thread components in favor of the new
widget-based approach

## Type fixes
- Add `EMAIL_THREAD` to `WidgetConfigurationType`, `WidgetType`, and all
configuration/validator maps
- Create `EmailThreadConfigurationDTO` and shared
`EmailThreadConfiguration` type
- Register EMAIL_THREAD in widget type validators, configuration
resolvers, and standard widget mappings

## Test plan
- [ ] Verify message thread record pages render with the email thread
widget
- [ ] Verify email thread preview navigates to the record page instead
of opening side panel
- [ ] Verify reply-to-email command appears for message thread records
- [ ] Verify typecheck passes for both twenty-front and twenty-server
- [ ] Run existing test suites to check for regressions

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Félix Malfait
2026-04-06 12:02:04 +02:00
committed by GitHub
parent 4aa1d71b12
commit 8acfacc69c
56 changed files with 2441 additions and 2371 deletions
@@ -0,0 +1,61 @@
import { DEFAULT_MESSAGE_THREAD_RECORD_PAGE_LAYOUT_ID } from '@/page-layout/constants/DefaultMessageThreadRecordPageLayoutId';
import { type PageLayout } from '@/page-layout/types/PageLayout';
import {
PageLayoutTabLayoutMode,
PageLayoutType,
WidgetConfigurationType,
WidgetType,
} from '~/generated-metadata/graphql';
export const DEFAULT_MESSAGE_THREAD_RECORD_PAGE_LAYOUT: PageLayout = {
__typename: 'PageLayout',
id: DEFAULT_MESSAGE_THREAD_RECORD_PAGE_LAYOUT_ID,
name: 'Default Message Thread Layout',
type: PageLayoutType.RECORD_PAGE,
objectMetadataId: null,
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
deletedAt: null,
tabs: [
{
__typename: 'PageLayoutTab',
applicationId: '',
id: 'message-thread-tab-home',
title: 'Home',
icon: 'IconHome',
position: 100,
layoutMode: PageLayoutTabLayoutMode.VERTICAL_LIST,
pageLayoutId: DEFAULT_MESSAGE_THREAD_RECORD_PAGE_LAYOUT_ID,
isOverridden: false,
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
deletedAt: null,
widgets: [
{
__typename: 'PageLayoutWidget',
id: 'message-thread-widget-email-thread',
pageLayoutTabId: 'message-thread-tab-home',
title: 'Thread',
type: WidgetType.EMAIL_THREAD,
objectMetadataId: null,
gridPosition: {
__typename: 'GridPosition',
row: 0,
column: 0,
rowSpan: 12,
columnSpan: 12,
},
configuration: {
__typename: 'FieldsConfiguration',
configurationType: WidgetConfigurationType.FIELDS,
viewId: null,
},
isOverridden: false,
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
deletedAt: null,
},
],
},
],
};
@@ -0,0 +1,2 @@
export const DEFAULT_MESSAGE_THREAD_RECORD_PAGE_LAYOUT_ID =
'default-message-thread-page-layout';