[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
@@ -113,13 +113,10 @@ export const ObjectOptionsDropdownRecordGroupsContent = () => {
>
{currentView?.key !== 'INDEX' && (
<>
<SelectableListItem
itemId="GroupBy"
onEnter={() => onContentChange('recordGroupFields')}
>
<SelectableListItem itemId="GroupBy">
<MenuItem
focused={selectedItemId === 'GroupBy'}
onClick={() => onContentChange('recordGroupFields')}
disabled
LeftIcon={IconLayoutList}
text={t`Group by`}
contextualText={recordGroupFieldMetadata?.label}
@@ -3,6 +3,7 @@ import { contextStoreCurrentViewIdComponentState } from '@/context-store/states/
import { type FieldMetadataItem } from '@/object-metadata/types/FieldMetadataItem';
import { useSetRecordGroups } from '@/object-record/record-group/hooks/useSetRecordGroups';
import { useRecoilComponentCallbackState } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentCallbackState';
import { usePersistView } from '@/views/hooks/internal/usePersistView';
import { usePersistViewGroupRecords } from '@/views/hooks/internal/usePersistViewGroup';
import { useGetViewFromPrefetchState } from '@/views/hooks/useGetViewFromPrefetchState';
import { type ViewGroup } from '@/views/types/ViewGroup';
@@ -23,6 +24,8 @@ export const useHandleRecordGroupField = () => {
const { setRecordGroupsFromViewGroups } = useSetRecordGroups();
const { updateView } = usePersistView();
const handleRecordGroupFieldChange = useRecoilCallback(
({ snapshot }) =>
async (fieldMetadataItem: FieldMetadataItem) => {
@@ -34,7 +37,7 @@ export const useHandleRecordGroupField = () => {
return;
}
const view = await getViewFromPrefetchState(currentViewId);
const view = getViewFromPrefetchState(currentViewId);
if (isUndefinedOrNull(view)) {
return;
@@ -47,6 +50,13 @@ export const useHandleRecordGroupField = () => {
return;
}
await updateView({
id: view.id,
input: {
mainGroupByFieldMetadataId: fieldMetadataItem.id,
},
});
const existingGroupKeys = new Set(
view.viewGroups.map(
(group) => `${group.fieldMetadataId}:${group.fieldValue}`,
@@ -124,10 +134,11 @@ export const useHandleRecordGroupField = () => {
}
},
[
objectMetadataItem,
currentViewIdCallbackState,
getViewFromPrefetchState,
updateView,
setRecordGroupsFromViewGroups,
objectMetadataItem,
createViewGroups,
deleteViewGroups,
],