Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: view group followup #9162

Open
wants to merge 16 commits into
base: main
Choose a base branch
from
Open

fix: view group followup #9162

wants to merge 16 commits into from

Conversation

magrinj
Copy link
Member

@magrinj magrinj commented Dec 20, 2024

This PR fixes all followup that @Bonapara add on Discord.

  • When no group by is set, clicking on group by should open the "field selection" menu
  • When closed, chevron should be "chevron-right" instead of "chevron-up"
  • Sort : Add ability to switch from alphabetical to manual when moving a option in sort alphabetical
  • Add subtext for group by and sort
  • Group by menu display bug
  • Changing the sort should not close the menu
  • Group by Activation -> shows empty state + is slow
  • Switching from Kanban view Settings to Table Options menu displays an empty menu
  • Unnecessary spacing under groups
  • When no "select" are set on an object, redirect the user directly to the new Select field page
  • Sort : Default should be manual
  • Hidding "no value" displays all options and remove the "hide empty group" toggle
  • Hide Empty group option disappeared
  • Group by should not be persisted on "Locked/Main view" (For now we just disable the group by on main view)
  • Hide Empty group should not be activated by default on Opportunities Kanban view
  • Animate the group opening/closing (We'll be done later)

Performance improvement:
https://github.com/user-attachments/assets/fd2acf66-0e56-45d0-8b2f-99c62e57d6f7
https://github.com/user-attachments/assets/80f1a2e1-9f77-4923-b85d-acb9cad96886

@magrinj magrinj marked this pull request as ready for review December 20, 2024 09:58
Copy link

TODOs/FIXMEs:

  • // FixMe: Using triggerCreateRecordsOptimisticEffect is actaully causing multiple records to be created: packages/twenty-front/src/modules/views/hooks/internal/usePersistViewGroupRecords.ts

Generated by 🚫 dangerJS against 259fecf

Copy link
Contributor

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PR Summary

This PR implements significant improvements to the group by and sort functionalities in Twenty's record management system. The changes focus on fixing UI/UX issues and enhancing performance.

  • Switched from component selectors to family selectors for record groups with explicit view type parameters, improving state management granularity and performance
  • Added confirmation modal for switching from alphabetical to manual sorting when reordering groups, with proper state handling via recordGroupPendingDragEndReorderState
  • Implemented view-specific behavior for hiding empty groups using recordIndexRecordGroupHideComponentFamilyState, with different defaults for Table (hidden) and Kanban (visible) views
  • Improved dropdown menu interactions by keeping menus open during sort changes and adding contextual text for group by and sort options
  • Optimized view group persistence by replacing individual Apollo mutations with batch operations for better performance

32 file(s) reviewed, 13 comment(s)
Edit PR Review Bot Settings | Greptile

Comment on lines +124 to +128
onClick={() =>
isDefined(recordGroupFieldMetadata)
? onContentChange('recordGroups')
: onContentChange('recordGroupFields')
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

style: Consider adding error handling in case recordGroupFieldMetadata is undefined but onContentChange fails

import { atom } from 'recoil';

export const recordGroupPendingDragEndReorderState =
atom<Parameters<OnDragEndResponder> | null>({
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

style: Consider adding a more descriptive type alias instead of using Parameters directly, to improve code readability and maintainability


export const recordGroupPendingDragEndReorderState =
atom<Parameters<OnDragEndResponder> | null>({
key: 'recordGroupPendingDragEndReorderState',
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

style: Consider adding a more descriptive key prefix like 'recordGroup/' to avoid potential key collisions with other atoms

isOpen={isReorderConfirmationModalOpen}
setIsOpen={setIsReorderConfirmationModalOpen}
title="Group sorting"
subtitle={`Would you like to remove ${recordGroupSort} group sorting`}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

syntax: Missing punctuation at end of subtitle text

Comment on lines +63 to +65
if (!pendingDragEndReorder) {
throw new Error('pendingDragEndReorder is not set');
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

logic: Error thrown here could cause UI to break if pendingDragEndReorder is cleared by another action. Consider graceful fallback

Comment on lines +51 to +52
default:
return a.position - b.position;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

style: default case should be explicit rather than falling through from Manual case, in case new sort types are added

Comment on lines +46 to +49
case RecordGroupSort.Alphabetical:
return a.title.localeCompare(b.title);
case RecordGroupSort.ReverseAlphabetical:
return b.title.localeCompare(a.title);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

style: localeCompare should include options for case sensitivity and locale to ensure consistent sorting across all browsers

Comment on lines +15 to +16
return false;
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

style: Default case returns false but could potentially handle unknown ViewTypes differently. Consider if this is the desired behavior for future view types.

Comment on lines 40 to 53
const updateViewGroupRecords = useCallback(
async (viewGroupsToUpdate: ViewGroup[]) => {
if (!viewGroupsToUpdate.length) return;

const mutationPromises = viewGroupsToUpdate.map((viewGroup) =>
apolloClient.mutate<{ updateViewGroup: ViewGroup }>({
mutation: updateOneRecordMutation,
variables: {
idToUpdate: viewGroup.id,
input: {
isVisible: viewGroup.isVisible,
position: viewGroup.position,
},
return viewGroupsToUpdate.map((viewGroup) =>
updateOneRecord({
idToUpdate: viewGroup.id,
updateOneRecordInput: {
isVisible: viewGroup.isVisible,
position: viewGroup.position,
},
// Avoid cache being updated with stale data
fetchPolicy: 'no-cache',
}),
);

const mutationResults = await Promise.all(mutationPromises);

// FixMe: Using triggerCreateRecordsOptimisticEffect is actaully causing multiple records to be created
mutationResults.forEach(({ data }) => {
const record = data?.['updateViewGroup'];

if (!record) return;

apolloClient.cache.modify({
id: apolloClient.cache.identify({
__typename: 'ViewGroup',
id: record.id,
}),
fields: {
isVisible: () => record.isVisible,
position: () => record.position,
},
});
});
},
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

logic: updateViewGroupRecords returns an array of promises but doesn't await them, which could lead to race conditions or incomplete updates

Comment on lines 57 to 66
const deleteViewGroupRecords = useCallback(
async (viewGroupsToDelete: ViewGroup[]) => {
if (!viewGroupsToDelete.length) return;

const mutationPromises = viewGroupsToDelete.map((viewGroup) =>
apolloClient.mutate<{ deleteViewGroup: ViewGroup }>({
mutation: deleteOneRecordMutation,
variables: {
idToDelete: viewGroup.id,
},
// Avoid cache being updated with stale data
fetchPolicy: 'no-cache',
}),
return destroyManyRecords(
viewGroupsToDelete.map((viewGroup) => viewGroup.id),
);

const mutationResults = await Promise.all(mutationPromises);

mutationResults.forEach(({ data }) => {
const record = data?.['deleteViewGroup'];

if (!record) return;

apolloClient.cache.evict({
id: apolloClient.cache.identify({
__typename: 'ViewGroup',
id: record.id,
}),
});
});
},
[apolloClient, deleteOneRecordMutation],
[destroyManyRecords],
);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

style: consider adding error handling for failed deletions since multiple records are being deleted in a batch

Copy link
Collaborator

@ijreilly ijreilly left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some remarks :)

1 - I observed a weird behaviour when creating a view group from an empty records page:
https://github.com/user-attachments/assets/26d4aa3f-6556-4bf4-8cd8-3638f1aaf719

2 - nit but in the dropdown on Sort, when opening the Ascending / Descending dropdown, the submenu is "merged" with the bigger dropdown, maybe not intended?
https://github.com/user-attachments/assets/56dacc61-a0df-498f-98a0-222e51b0841f

/>
)}
{(viewType === ViewType.Kanban || isViewGroupEnabled) &&
currentView?.key !== 'INDEX' && (
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we have this change ? I can't see the group by option from the table view, had to remove it to be able to create a view group

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh I see, it is to disable the option from the main view right ?
I feel this may prevent users from discovering the feature. Instead I would create a new view when selecting group by. wdyt @Bonapara ?

);

const hiddenRecordGroupIds = useRecoilComponentValueV2(
hiddenRecordGroupIdsComponentSelector,
);

const isDragableSortRecordGroup = useRecoilComponentValueV2(
recordIndexRecordGroupIsDraggableSortComponentSelector,
const hideEmptyRecordGroup = useRecoilComponentFamilyValueV2(
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

just a thought - are we sure we want to hide the empty groups by default? I would have done the opposite, otherwise it can feel like they are just missing (that was my impression)
@Bonapara

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ijreilly It's only hidden by default on the table view

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes but it means you have to create a new view to access it. I was just pointing out that I wasnt sure that is what was intended, i will let thomas clarify !

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It was actually requested by @Bonapara 🙂

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I said that since Notion hides empty groups by default, I think it's a safe behavior that prevents clutter, but this can change in the future!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants