fix(front): reload record board groups when view groups change (#23637)
## Problem Fixes #23462 On a Kanban (record board) view grouped by a SELECT field, adding a new option to that field creates the column, but dragging a record into the new column silently fails (no move, no error) until a hard page reload. ## Root cause `RecordIndexLoadBaseOnContextStoreEffect` builds its load key from the view id and the calendar-week flag only: ``` `${contextStoreCurrentViewId}-${isCalendarWeekViewEnabled}` ``` The effect early-returns when `loadedViewKey === currentViewLoadKey`. When a new `ViewGroup` is created from the added SELECT option, the view id does not change, so the key is unchanged and `loadRecordIndexStates` never re-runs. The record group state (`recordGroupIdsComponentState` / `recordGroupDefinitionFamilyState`) stays stale, so the drop handler cannot resolve the new group's field value and the move no-ops. A hard reload fixes it because the view then loads with the new group present from the start. ## Fix Include a signature of `view.viewGroups` (ordered `id:position:isVisible`) in the load key so the effect re-runs `loadRecordIndexStates` whenever the view's groups change, not only when the view id changes. The calendar-week flag is kept in the key. ## Testing Verified end to end on a local instance against an Opportunities "By Stage" Kanban, with the record's stage change confirmed in the database: - **Before the fix:** add a new Stage option in-session, then drag a record into the new column. Dragging into an existing column persists the move; dragging into the newly created column does nothing (record's stage unchanged in DB). - **After the fix:** same flow, dragging a record into the newly created column moves it and persists the new stage in DB, with no reload. `nx typecheck twenty-front` and `oxlint` pass. <!-- This is an auto-generated description by cubic. --> <a href="https://cubic.dev/pr/twentyhq/twenty/pull/23637?utm_source=github" target="_blank" rel="noopener noreferrer" data-no-image-dialog="true"><picture><source media="(prefers-color-scheme: dark)" srcset="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"><source media="(prefers-color-scheme: light)" srcset="https://www.cubic.dev/buttons/review-in-cubic-light.svg"><img alt="Review in cubic" src="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"></picture></a> <!-- End of auto-generated description by cubic. -->
This commit is contained in:
+9
-4
@@ -18,16 +18,21 @@ export const RecordIndexLoadBaseOnContextStoreEffect = () => {
|
||||
FeatureFlagKey.IS_CALENDAR_WEEK_VIEW_ENABLED,
|
||||
);
|
||||
|
||||
const currentViewLoadKey = isDefined(contextStoreCurrentViewId)
|
||||
? `${contextStoreCurrentViewId}-${isCalendarWeekViewEnabled}`
|
||||
: undefined;
|
||||
|
||||
const [loadedViewKey, setLoadedViewKey] = useState<string | undefined>();
|
||||
|
||||
const view = useAtomFamilySelectorValue(viewFromViewIdFamilySelector, {
|
||||
viewId: contextStoreCurrentViewId ?? '',
|
||||
});
|
||||
|
||||
const viewGroupsSignature = (view?.viewGroups ?? [])
|
||||
.map((viewGroup) => viewGroup.id)
|
||||
.sort()
|
||||
.join(',');
|
||||
|
||||
const currentViewLoadKey = isDefined(contextStoreCurrentViewId)
|
||||
? `${contextStoreCurrentViewId}-${isCalendarWeekViewEnabled}-${viewGroupsSignature}`
|
||||
: undefined;
|
||||
|
||||
const { objectMetadataItem } = useContextStoreObjectMetadataItemOrThrow();
|
||||
|
||||
useEffect(() => {
|
||||
|
||||
+43
@@ -84,4 +84,47 @@ describe('RecordIndexLoadBaseOnContextStoreEffect', () => {
|
||||
FeatureFlagKey.IS_CALENDAR_WEEK_VIEW_ENABLED,
|
||||
);
|
||||
});
|
||||
|
||||
it('reloads the persisted view state when the view groups change', () => {
|
||||
const viewWithGroups = {
|
||||
id: 'view-id',
|
||||
viewGroups: [{ id: 'group-1' }],
|
||||
};
|
||||
useAtomFamilySelectorValueMock.mockReturnValue(viewWithGroups);
|
||||
|
||||
const { rerender } = render(<RecordIndexLoadBaseOnContextStoreEffect />);
|
||||
|
||||
expect(loadRecordIndexStates).toHaveBeenCalledTimes(1);
|
||||
|
||||
rerender(<RecordIndexLoadBaseOnContextStoreEffect />);
|
||||
|
||||
expect(loadRecordIndexStates).toHaveBeenCalledTimes(1);
|
||||
|
||||
useAtomFamilySelectorValueMock.mockReturnValue({
|
||||
...viewWithGroups,
|
||||
viewGroups: [...viewWithGroups.viewGroups, { id: 'group-2' }],
|
||||
});
|
||||
rerender(<RecordIndexLoadBaseOnContextStoreEffect />);
|
||||
|
||||
expect(loadRecordIndexStates).toHaveBeenCalledTimes(2);
|
||||
});
|
||||
|
||||
it('does not reload when the view groups are only reordered', () => {
|
||||
useAtomFamilySelectorValueMock.mockReturnValue({
|
||||
id: 'view-id',
|
||||
viewGroups: [{ id: 'group-1' }, { id: 'group-2' }],
|
||||
});
|
||||
|
||||
const { rerender } = render(<RecordIndexLoadBaseOnContextStoreEffect />);
|
||||
|
||||
expect(loadRecordIndexStates).toHaveBeenCalledTimes(1);
|
||||
|
||||
useAtomFamilySelectorValueMock.mockReturnValue({
|
||||
id: 'view-id',
|
||||
viewGroups: [{ id: 'group-2' }, { id: 'group-1' }],
|
||||
});
|
||||
rerender(<RecordIndexLoadBaseOnContextStoreEffect />);
|
||||
|
||||
expect(loadRecordIndexStates).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user