Skip to content

Role page various fixes 2 #12416

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

Merged
merged 2 commits into from
Jun 2, 2025
Merged

Role page various fixes 2 #12416

merged 2 commits into from
Jun 2, 2025

Conversation

Weiko
Copy link
Member

@Weiko Weiko commented Jun 2, 2025

  • Fix: AvatarURL signedPath for workspace members were not consistent when queried multiple times and it was causing the frontend to wrongly interpret this as a change in the deepEqual condition
  • Use SaveAndCancel button to be consistent with data model page
  • When applying all object permission changes, a "smarter" logic applies and removes all permissions if read is unchecked for example
  • Hide settings permissions when Settings All Access is toggled

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 addresses role management and avatar URL inconsistencies, focusing on workspace member data handling and permissions logic. Here are the key changes:

  • Fixed avatar URL signedPath inconsistency by removing server-side signing and using enriched workspace member data from Recoil state
  • Implemented smarter permission cascade logic where disabling read access automatically disables other permissions (update, soft delete, destroy)
  • Added collapsible settings permissions section that hides when 'Settings All Access' is enabled
  • Replaced single Save button with SaveAndCancelButtons component for consistency with data model page
  • Improved role assignment workflow by using CurrentWorkspaceMember type instead of SearchRecord for better type safety

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

Comment on lines +43 to +45
if (!isDeeplyEqual(currentDraftRole, role)) {
set(settingsDraftRoleFamilyState(role.id), role);
}
Copy link
Contributor

Choose a reason for hiding this comment

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

logic: This check prevents unnecessary updates but could mask legitimate changes if currentDraftRole contains local modifications that should be overwritten by the server state.

Comment on lines +49 to +53
placeholder: workspaceMember?.name.firstName ?? '',
placeholderColorSeed: workspaceMember?.id,
avatarUrl: workspaceMember?.avatarUrl,
}}
text={workspaceMember.label}
text={workspaceMember?.name.firstName ?? ''}
Copy link
Contributor

Choose a reason for hiding this comment

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

style: Optional chaining is redundant here since isDefined filter was already applied. Remove the optional chaining operators.

Suggested change
placeholder: workspaceMember?.name.firstName ?? '',
placeholderColorSeed: workspaceMember?.id,
avatarUrl: workspaceMember?.avatarUrl,
}}
text={workspaceMember.label}
text={workspaceMember?.name.firstName ?? ''}
placeholder: workspaceMember.name.firstName ?? '',
placeholderColorSeed: workspaceMember.id,
avatarUrl: workspaceMember.avatarUrl,
}}
text={workspaceMember.name.firstName ?? ''}

Comment on lines +74 to +78
...(value === true
? {
canReadAllObjectRecords: value,
}
: {}),
Copy link
Contributor

Choose a reason for hiding this comment

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

style: This logic is repeated for multiple permissions. Consider extracting to a shared utility function to maintain DRY principles and ensure consistent behavior

Copy link
Contributor

github-actions bot commented Jun 2, 2025

🚀 Preview Environment Ready!

Your preview environment is available at: http://bore.pub:46042

This environment will automatically shut down when the PR is closed or after 5 hours.

@@ -188,17 +188,6 @@ export class RoleResolver {
workspace.id,
);

await Promise.all(
Copy link
Member Author

Choose a reason for hiding this comment

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

This was added in the last PR to include avatarUrls to the response but this is what broke the dirty state in the FE due to the sign being different after multiple requests (which could happen but the FE was able to not update the state via a isDeeplyEqual but this function was always returning false due to the avatarUrl containing the different hash). I'm reverting this and now we use the existing currentWorkspaceMembersState state to retrieve the correct avatarURL. I think this issue is not only scoped to permissions though and could happen in other surfaces 👀

Copy link
Collaborator

Choose a reason for hiding this comment

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

"but this is what broke the dirty state in the FE due to the sign being different"
What is the sign?

Copy link
Member Author

Choose a reason for hiding this comment

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

When you query the API to retrieve an image, we need to sign it so the user can view its content, it looks like this:
http://localhost:3000/files/profile-picture/original/eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJmaWxlbmFtZSI6ImE5MDRhOTk4LTY0YjItNDcx....
which is then verified on the backend when opened to see if the image has been correctly signed (and it has an expiration date).
This encoding sign was added every time we were doing getRoles and is not idempotent (each query was returning a different sign) so the payload was always different even though the role was not changed

set(settingsPersistedRoleFamilyState(role.id), role);

if (!isDeeplyEqual(currentDraftRole, role)) {
Copy link
Member Author

Choose a reason for hiding this comment

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

This is probably redundant actually, the root cause was the avatarUrl thing, I can remove this

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.

maybe introduced by the form update ? (on a role i m creating)

Enregistrement.de.l.ecran.2025-06-02.a.16.49.16.mov

@@ -62,6 +65,16 @@ export const SettingsRolesTableRow = ({ role }: SettingsRolesTableRowProps) => {
const { getIcon } = useIcons();
const Icon = getIcon(role.icon ?? 'IconUser');

const currentWorkspaceMembers = useRecoilValue(currentWorkspaceMembersState);

const enrichedWorkspaceMembers = role.workspaceMembers
Copy link
Collaborator

Choose a reason for hiding this comment

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

what does enrich mean here ?

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 means the workspaceMember returned by role only contains the id then we "enrich" it through the currentWorkspaceMembersState to get the rest such as the avatarUrl (with a sign in the URL that's not supposed to change since getCurrentUser is only called when we refresh the page)

Copy link
Member Author

Choose a reason for hiding this comment

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

I can change the name if it's not clear!

id: workspaceMemberSearchRecord.recordId,
name: `${workspaceMemberSearchRecord.label}`,
id: workspaceMember.id,
name: `${workspaceMember.name.firstName} ${workspaceMember.name.lastName}`,
Copy link
Collaborator

Choose a reason for hiding this comment

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

are these always present (they are nullable in the db)? if not we may be displaying "undefined"

Copy link
Collaborator

Choose a reason for hiding this comment

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

nevermind, they are nullable in the db but not in the schema

Copy link
Member Author

Choose a reason for hiding this comment

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

They could be empty strings in theory but not undefined yes, I believe label from the search query is already the concatenation of firstName + lastName so the result should be the same

<AnimatedExpandableContainer
isExpanded={!settingsDraftRole.canUpdateAllSettings}
dimension="height"
animationDurations={{
Copy link
Collaborator

Choose a reason for hiding this comment

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

should these be in a constant ? I suppose we would like all animations to be similar through the app

Copy link
Member Author

Choose a reason for hiding this comment

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

There is one constant ADVANCED_SETTINGS_ANIMATION_DURATION I believe, I can rename it to make it more generic if we indeed want to it be consistant through the app or create a new one

@@ -188,17 +188,6 @@ export class RoleResolver {
workspace.id,
);

await Promise.all(
Copy link
Collaborator

Choose a reason for hiding this comment

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

"but this is what broke the dirty state in the FE due to the sign being different"
What is the sign?

@Weiko
Copy link
Member Author

Weiko commented Jun 2, 2025

maybe introduced by the form update ? (on a role i m creating)

Enregistrement.de.l.ecran.2025-06-02.a.16.49.16.mov

I'm not sure this is new, I'll handle this in another ticket but when creating a role the input (where you see "Role Name" at the top) should be opened and force you to type something otherwise the role name will be empty (even though we display a placeholder "Role Name", actually the placeholder should also have a different color, that's also for the other ticket).
A more pragmatic approach: We could also set and send "Role name" during creation and the next time someone tries to create a role it will say the role name is already taken

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.

great ! role creation / update form is much clearer now i feel !

@Weiko Weiko merged commit 8e71000 into main Jun 2, 2025
57 checks passed
@Weiko Weiko deleted the c--fe-role-various-fixes-2 branch June 2, 2025 18:24
abdulrahmancodes pushed a commit to abdulrahmancodes/twenty that referenced this pull request Jun 2, 2025
- Fix: AvatarURL signedPath for workspace members were not consistent
when queried multiple times and it was causing the frontend to wrongly
interpret this as a change in the deepEqual condition
- Use SaveAndCancel button to be consistent with data model page
- When applying all object permission changes, a "smarter" logic applies
and removes all permissions if read is unchecked for example
- Hide settings permissions when Settings All Access is toggled
abdulrahmancodes pushed a commit to abdulrahmancodes/twenty that referenced this pull request Jun 3, 2025
- Fix: AvatarURL signedPath for workspace members were not consistent
when queried multiple times and it was causing the frontend to wrongly
interpret this as a change in the deepEqual condition
- Use SaveAndCancel button to be consistent with data model page
- When applying all object permission changes, a "smarter" logic applies
and removes all permissions if read is unchecked for example
- Hide settings permissions when Settings All Access is toggled
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