[Requires "warm" cache flush (no immediate downtime before flush)] Migrate viewGroup.fieldMetadataId -> view.mainGroupByFieldMetadataId (1/3) (#16206)

In this PR (1/3)
- introduce view.mainGroupByFieldMetadataId as the new reference
determining which fieldMetadataId is used in a grouped view, in order to
deprecate viewGroup.fieldMetadataId which creates inconsistencies.
view.mainGroupByFieldMetadataId is now filled at every view creation,
though not in use yet.
- Introduce a command to backfill view.mainGroupByFieldMetadataId for
existing views + delete all viewGroup.fieldMetadataId with a
fieldMetadataId that is not view.mainGroupByFieldMetadataId. (It should
concern 37 active workspaces)
- Temporarily disable the option to change a grouped view's
fieldMetadataId as for now it creates inconsistencies. This feature can
be reintroduced when we have done the full migration.

In a next PR
- (2/3) use view.mainGroupByFieldMetadataId instead of
viewGroup.fieldMetadataId. In FE we may keep viewGroup.fieldMetadataId
as a state (TBD). View groups will now be created / deleted as a side
effect of view's mainGroupByFieldMetadataId update.
- (3/3) remove viewGroup.fieldMetadataId

---------

Co-authored-by: Charles Bochet <charles@twenty.com>
This commit is contained in:
Marie
2025-12-02 16:31:07 +01:00
committed by GitHub
parent aa729a2a0a
commit 77409b6eb2
71 changed files with 484 additions and 99 deletions
@@ -171,6 +171,7 @@ export const createCoreViews = async (
icon,
isCustom,
openRecordIn,
mainGroupByFieldMetadataId,
kanbanAggregateOperation,
kanbanAggregateOperationFieldMetadataId,
applicationId,
@@ -191,6 +192,7 @@ export const createCoreViews = async (
: ViewOpenRecordIn.SIDE_PANEL,
kanbanAggregateOperation,
kanbanAggregateOperationFieldMetadataId,
mainGroupByFieldMetadataId,
workspaceId,
anyFieldFilterValue: null,
visibility: ViewVisibility.WORKSPACE,
@@ -16,9 +16,9 @@ export interface ViewDefinition {
icon?: string;
isCustom?: boolean;
openRecordIn?: ViewOpenRecordInType;
kanbanFieldMetadataId?: string;
kanbanAggregateOperation?: AggregateOperations;
kanbanAggregateOperationFieldMetadataId?: string;
mainGroupByFieldMetadataId?: string;
calendarFieldMetadataId?: string;
calendarLayout?: string;
fields?: {
@@ -42,7 +42,6 @@ export const calendarEventsAllView = ({
key: 'INDEX',
position: 0,
icon: 'IconCalendar',
kanbanFieldMetadataId: '',
calendarFieldMetadataId:
calendarEventObjectMetadata.fields.find(
(field) =>
@@ -43,7 +43,6 @@ export const companiesAllView = ({
key: 'INDEX',
position: 0,
icon: 'IconList',
kanbanFieldMetadataId: '',
filters: [],
fields: [
{
@@ -42,7 +42,6 @@ export const dashboardsAllView = ({
key: 'INDEX',
position: 0,
icon: 'IconLayoutDashboard',
kanbanFieldMetadataId: '',
openRecordIn: ViewOpenRecordInType.RECORD_PAGE,
filters: [],
fields: [
@@ -42,7 +42,6 @@ export const messageThreadsAllView = ({
key: 'INDEX',
position: 0,
icon: 'IconList',
kanbanFieldMetadataId: '',
filters: [],
fields: [
{
@@ -42,7 +42,6 @@ export const messagesAllView = ({
key: 'INDEX',
position: 0,
icon: 'IconList',
kanbanFieldMetadataId: '',
filters: [],
fields: [
{
@@ -41,7 +41,6 @@ export const notesAllView = ({
key: 'INDEX',
position: 0,
icon: 'IconNotes',
kanbanFieldMetadataId: '',
filters: [],
fields: [
{
@@ -39,7 +39,6 @@ export const opportunitiesAllView = ({
key: 'INDEX',
position: 0,
icon: 'IconList',
kanbanFieldMetadataId: '',
filters: [],
fields: [
{
@@ -29,6 +29,11 @@ export const opportunitiesByStageView = ({
const viewUniversalIdentifier =
STANDARD_OBJECTS.opportunity.views.byStage.universalIdentifier;
const stageFieldMetadataId =
opportunityObjectMetadata.fields.find(
(field) => field.standardId === OPPORTUNITY_STANDARD_FIELD_IDS.stage,
)?.id ?? '';
return {
id: v4(),
universalIdentifier: viewUniversalIdentifier,
@@ -39,16 +44,13 @@ export const opportunitiesByStageView = ({
key: null,
position: 2,
icon: 'IconLayoutKanban',
kanbanFieldMetadataId:
opportunityObjectMetadata.fields.find(
(field) => field.standardId === OPPORTUNITY_STANDARD_FIELD_IDS.stage,
)?.id ?? '',
kanbanAggregateOperation: AggregateOperations.MIN,
kanbanAggregateOperationFieldMetadataId:
opportunityObjectMetadata.fields.find(
(field) => field.standardId === OPPORTUNITY_STANDARD_FIELD_IDS.amount,
)?.id ?? '',
filters: [],
mainGroupByFieldMetadataId: stageFieldMetadataId,
fields: [
{
fieldMetadataId:
@@ -42,7 +42,6 @@ export const peopleAllView = ({
key: 'INDEX',
position: 0,
icon: 'IconList',
kanbanFieldMetadataId: '',
filters: [],
fields: [
{
@@ -41,7 +41,6 @@ export const tasksAllView = ({
key: 'INDEX',
position: 0,
icon: 'IconList',
kanbanFieldMetadataId: '',
filters: [] /* [
{
fieldMetadataId:
@@ -42,7 +42,10 @@ export const tasksAssignedToMeView = ({
key: null,
position: 2,
icon: 'IconUserCircle',
kanbanFieldMetadataId: '',
mainGroupByFieldMetadataId:
taskObjectMetadata.fields.find(
(field) => field.standardId === TASK_STANDARD_FIELD_IDS.status,
)?.id ?? '',
filters: [
{
fieldMetadataId:
@@ -41,7 +41,7 @@ export const tasksByStatusView = ({
key: null,
position: 1,
icon: 'IconLayoutKanban',
kanbanFieldMetadataId:
mainGroupByFieldMetadataId:
taskObjectMetadata.fields.find(
(field) => field.standardId === TASK_STANDARD_FIELD_IDS.status,
)?.id ?? '',
@@ -44,7 +44,6 @@ export const timelineActivitiesAllView = ({
position: 0,
icon: 'IconList',
openRecordIn: ViewOpenRecordInType.RECORD_PAGE,
kanbanFieldMetadataId: '',
filters: [],
fields: [
{
@@ -40,7 +40,6 @@ export const workflowRunsAllView = ({
position: 0,
icon: 'IconHistoryToggle',
openRecordIn: ViewOpenRecordInType.RECORD_PAGE,
kanbanFieldMetadataId: '',
filters: [],
fields: [
{
@@ -46,7 +46,6 @@ export const workflowVersionsAllView = ({
position: 0,
icon: 'IconVersions',
openRecordIn: ViewOpenRecordInType.RECORD_PAGE,
kanbanFieldMetadataId: '',
filters: [],
fields: [
{
@@ -43,7 +43,6 @@ export const workflowsAllView = ({
position: 0,
icon: 'IconSettingsAutomation',
openRecordIn: ViewOpenRecordInType.RECORD_PAGE,
kanbanFieldMetadataId: '',
filters: [],
fields: [
{
@@ -45,7 +45,6 @@ export const workspaceMembersAllView = ({
key: 'INDEX',
position: 0,
icon: 'IconList',
kanbanFieldMetadataId: '',
filters: [],
fields: [
{