-
Notifications
You must be signed in to change notification settings - Fork 2.2k
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
feat(invitation): Improve invitation flow - Milestone 2 #6804
feat(invitation): Improve invitation flow - Milestone 2 #6804
Conversation
Log
|
3ff8377
to
6e1d51b
Compare
eaaa88e
to
f46d4fd
Compare
@FelixMalfait How can I test the Google Connect and the Microsoft connect feature? |
f46d4fd
to
0ea8493
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
PR Summary
This pull request implements significant improvements to the invitation flow, focusing on personal invitations with expiration dates and better management of workspace members. Here's a summary of the key changes:
- Added support for personal invitation tokens in the sign-up process
- Implemented expiration dates for invitations with the ability to renew them
- Updated the UI to display, manage, and resend workspace invitations
- Improved error handling for invalid or expired invitations
- Modified database schema to support the new invitation system
- Enhanced the invitation process for both email and social login methods
Key points:
- New
AppTokenType.InvitationToken
added to support personal invitations WorkspaceInviteTeam
component now usesuseInviteUser
hook for improved invitation handlingSettingsWorkspaceMembers
component significantly refactored to manage invitations and members- Added GraphQL mutations for deleting and resending workspace invitations
- Implemented
useWorkspaceFromInviteHash
hook to handle invitation logic consistently
29 file(s) reviewed, 13 comment(s)
Edit PR Review Bot Settings
@@ -295,21 +297,44 @@ export const useAuth = () => { | |||
[setIsVerifyPendingState, signUp, handleVerify], | |||
); | |||
|
|||
const handleGoogleLogin = useCallback((workspaceInviteHash?: string) => { | |||
// TODO: how to test that? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
style: Remove TODO comment and implement proper testing for this function
packages/twenty-front/src/modules/auth/sign-in-up/hooks/useSignInWithGoogle.ts
Show resolved
Hide resolved
packages/twenty-front/src/modules/auth/sign-in-up/hooks/useSignInWithMicrosoft.ts
Show resolved
Hide resolved
packages/twenty-front/src/modules/workspace-invitation/hooks/useInviteUser.ts
Outdated
Show resolved
Hide resolved
); | ||
|
||
await queryRunner.query( | ||
`ALTER TABLE core."appToken" ADD CONSTRAINT "userIdNotNullWhenTypeIsNotInvitation" CHECK ("appToken".type = 'INVITATION_TOKEN' OR "appToken"."userId" NOTNULL)`, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
syntax: NOTNULL is non-standard SQL. Use IS NOT NULL instead.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
agree with greptile!
packages/twenty-server/src/database/typeorm/core/migrations/1724056827317-addInvitation.ts
Show resolved
Hide resolved
packages/twenty-server/src/database/typeorm/core/migrations/1724056827317-addInvitation.ts
Show resolved
Hide resolved
packages/twenty-server/src/database/typeorm/core/migrations/1724056827317-addInvitation.ts
Show resolved
Hide resolved
9ceb7c4
to
412af8a
Compare
412af8a
to
391f19a
Compare
6d985ec
to
15cb5e4
Compare
15cb5e4
to
a1c9f4f
Compare
c809c4c
to
59903c3
Compare
|
||
import { useAuth } from '@/auth/hooks/useAuth'; | ||
|
||
export const useSignInWithMicrosoft = () => { | ||
const workspaceInviteHash = useParams().workspaceInviteHash; | ||
const [searchParams] = useSearchParams(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM but I feel this could have been extracted to a hook as it's duplicated 3 times!
@@ -41,10 +43,12 @@ export const TableRow = ({ | |||
to, | |||
className, | |||
children, | |||
gridAutoColumns, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I feel we could have found a better naming, it's a bit too "technical" from the outside point of view
export const useCreateWorkspaceInvitation = () => { | ||
const [sendInvitationsMutation] = useSendInvitationsMutation(); | ||
|
||
const [, setWorkspaceInvitations] = useRecoilState(workspaceInvitationsState); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
useSetRecoilState!
Unfortunately, if you use useRecoilState you are doing an underlying useRecoilValue and useSetRecoilState even if you don't use the value
const [deleteWorkspaceInvitationMutation] = | ||
useDeleteWorkspaceInvitationMutation(); | ||
|
||
const [, setWorkspaceInvitations] = useRecoilState(workspaceInvitationsState); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
same here
const [resendWorkspaceInvitationMutation] = | ||
useResendWorkspaceInvitationMutation(); | ||
|
||
const [, setWorkspaceInvitations] = useRecoilState(workspaceInvitationsState); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
idem
@@ -24,3 +24,10 @@ export type WorkspaceMember = { | |||
dateFormat?: WorkspaceMemberDateFormatEnum | null; | |||
timeFormat?: WorkspaceMemberTimeFormatEnum | null; | |||
}; | |||
|
|||
export type WorkspaceInvitation = { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this could be in workspace-invitation module
import { isDefined } from '~/utils/isDefined'; | ||
|
||
const StyledContainer = styled.div` | ||
display: flex; | ||
flex-direction: row; | ||
padding-bottom: 12px; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
use theme here!
`; | ||
|
||
const StyledTableHeaderRow = styled(Table)` | ||
margin-bottom: ${({ theme }) => theme.spacing(1.5)}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
it's a bit a code smell that you are using .5 units
Usually if you have that is that the design meant that the height was important and not the margin
private readonly userWorkspaceService: UserWorkspaceService, | ||
private readonly workspaceInvitationService: WorkspaceInvitationService, | ||
) {} | ||
|
||
@Mutation(() => User) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we will likely need to rename this mutation to be consistent
addUserToWorkspaceByInviteHash
) {} | ||
|
||
onModuleInit() { | ||
this.tokenService = this.moduleRef.get(TokenService, { strict: false }); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this does not look standard, why not using dependency injection?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is a very good one!
Congrats on this huge PR and thanks for all the attention to details, for following our project guidelines and for the fix post reviews!
I have added small comments that are non merge blocking and push little changes so we can merge it as you have been waiting long enough :p
.getOne(); | ||
} | ||
|
||
castAppTokenToWorkspaceInvitation(appToken: AppToken) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
could be extracted to an util
@@ -0,0 +1,17 @@ | |||
import { CustomException } from 'src/utils/custom-exception'; | |||
|
|||
export class WorkspaceInvitationException extends CustomException { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nice!
color: ${({ theme, color }) => color ?? theme.font.color.primary}; | ||
`; | ||
|
||
export const StyledText = ({ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
usually we don't export component named "Styled" even if re-usable. Outside consumers do not care it's a StyledComponent or not!
fdef1e5
to
edad971
Compare
From PR: #6626
Resolves #6763
Resolves #6055
Resolves #6782
GTK
I retain the 'Invite by link' feature to prevent any breaking changes. We could make the invitation by link optional through an admin setting, allowing users to rely solely on personal invitations.
Todo