Feat/invite user confirmation#397
Conversation
There was a problem hiding this comment.
Pull request overview
Adds UI/state support in the React InviteUser v2 flow to detect when an invitation email was sent and show a dedicated confirmation state, while keeping the invite-link sharing flow as a fallback.
Changes:
- Add
isEmailSenttoBaseInviteUserrender props and trackemailSentstate fromadditionalData.emailSent. - Render a new success card when an invite email is detected as successfully sent.
- Add a changeset to publish the update as a patch for
@asgardeo/react.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.
| File | Description |
|---|---|
| packages/react/src/components/presentation/auth/InviteUser/v2/BaseInviteUser.tsx | Tracks emailSent from flow responses and adds an email-sent success UI branch. |
| .changeset/rude-zebras-dress.md | Declares a patch release note for the new invite-via-email capability. |
Comments suppressed due to low confidence (1)
packages/react/src/components/presentation/auth/InviteUser/v2/BaseInviteUser.tsx:454
- In
handleSubmit,inviteLink/emailSentstate is updated before checkingresponse.flowStatus === 'ERROR'. If the backend returnsadditionalData.inviteLinkoremailSentalongside an error (or a stale value), the component can end up rendering a success state even though an error was reported. Consider checkingflowStatusand handling errors before mutating success-related state, or explicitly clearinginviteLink/emailSentwhen an error response is received.
// Check if invite link is in the response
if (response.data?.additionalData?.['inviteLink']) {
const inviteLinkValue: any = response.data.additionalData['inviteLink'];
setInviteLink(inviteLinkValue);
onInviteLinkGenerated?.(inviteLinkValue, response.flowId);
}
// Check if email was sent successfully
if (response.data?.additionalData?.['emailSent'] === 'true') {
setEmailSent(true);
}
// Check for error status
if (response.flowStatus === 'ERROR') {
handleError(response);
return;
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| // Invite email sent successfully - show email sent confirmation | ||
| if (isInviteGenerated && isEmailSent) { | ||
| return ( | ||
| <CardPrimitive className={cx(className, styles.card)} variant={variant}> | ||
| <CardPrimitive.Header className={styles.header}> | ||
| <CardPrimitive.Title level={2} className={styles.title}> | ||
| Invite Email Sent! | ||
| </CardPrimitive.Title> | ||
| </CardPrimitive.Header> | ||
| <CardPrimitive.Content> | ||
| <AlertPrimitive variant="success"> | ||
| <AlertPrimitive.Description> | ||
| An invitation email has been sent successfully. The user can complete their registration using the link in | ||
| the email. | ||
| </AlertPrimitive.Description> | ||
| </AlertPrimitive> | ||
| <div style={{display: 'flex', gap: '0.5rem', marginTop: '1.5rem'}}> | ||
| <Button variant="outline" onClick={resetFlow}> | ||
| Invite Another User | ||
| </Button> | ||
| </div> | ||
| </CardPrimitive.Content> | ||
| </CardPrimitive> | ||
| ); | ||
| } |
There was a problem hiding this comment.
The email-sent confirmation UI is currently gated by isInviteGenerated && isEmailSent, but isInviteGenerated is derived from inviteLink. If the platform successfully sends an email invitation without returning an inviteLink in additionalData, the component will never show the email confirmation state (even though emailSent is true). Consider keying this branch primarily off isEmailSent (and only using inviteLink for the fallback copy-link path).
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
|
@RandithaK can you fix lint issues reported in |
Fixed |
🦋 Changeset detectedThe changes in this PR will be included in the next version bump. Not sure what this means? Click here to learn what changesets are. |
|
We'll need to do some improvements later. But merging current changes due to urgency |
| <CardPrimitive className={cx(className, styles.card)} variant={variant}> | ||
| <CardPrimitive.Header className={styles.header}> | ||
| <CardPrimitive.Title level={2} className={styles.title}> | ||
| Invite Email Sent! |
There was a problem hiding this comment.
All user-facing strings should go through t() hook to support i18n and translations.
The existing "Invite Link Generated!" block also hardcodes strings. That's a pre-existing bug, let's fix it with this effort as well.
|
|
||
| // Check if email was sent successfully | ||
| if (response.data?.additionalData?.['emailSent'] === 'true') { | ||
| setEmailSent(true); |
There was a problem hiding this comment.
If the server returns BOTH emailSent: 'true' AND flowStatus: 'ERROR', you'll set emailSent = true and then also trigger error handling. The UI will show the email-sent success card because isInviteGenerated && isEmailSent is checked first in the render. The user will see a success message for a failed flow which is incorrect.
Move the emailSent check to AFTER the error check, or guard it with response.flowStatus !== 'ERROR'.
| const {title, subtitle} = extractHeadings(components); | ||
| const componentsWithoutHeadings: any = filterHeadings(components); | ||
| const isInviteGenerated: any = !!inviteLink; | ||
| const isEmailSent: boolean = emailSent; |
There was a problem hiding this comment.
emailSent is already a boolean from useState(false). Here you are creating a new variable with the same type and same value.
Better to rename the state itself to isEmailSent as it follows convention and use it.
| the email. | ||
| </AlertPrimitive.Description> | ||
| </AlertPrimitive> | ||
| <div style={{display: 'flex', gap: '0.5rem', marginTop: '1.5rem'}}> |
There was a problem hiding this comment.
This file has a dedicated BaseInviteUser.styles.ts that uses @emotion/css with theme tokens. Your 0.5rem and 1.5rem should use theme.vars.spacing.unit like the rest of the styles file does. Hardcoded pixel/rem values break when someone changes the spacing scale and will hinder if we want to override these styles from branding etc.
| <CardPrimitive className={cx(className, styles.card)} variant={variant}> | ||
| <CardPrimitive.Header className={styles.header}> | ||
| <CardPrimitive.Title level={2} className={styles.title}> | ||
| Invite Email Sent! |
There was a problem hiding this comment.
Your new email-sent block is nearly identical to the existing invite-link block.
This is copy-paste with different text. Extract a shared SuccessCard component or at minimum a helper that takes title, description, and optional extra content. The invite-link block could pass the copy-link section as children.
| @@ -624,6 +636,7 @@ const BaseInviteUser: FC<BaseInviteUserProps> = ({ | |||
| const {title, subtitle} = extractHeadings(components); | |||
| const componentsWithoutHeadings: any = filterHeadings(components); | |||
There was a problem hiding this comment.
Not related to your changes, but when you fix the above-mentioned issues, better to fix these any typed variables to have a proper types.
| const [touchedFields, setTouchedFields] = useState<Record<string, boolean>>({}); | ||
| const [inviteLink, setInviteLink] = useState<string | undefined>(); | ||
| const [inviteLinkCopied, setInviteLinkCopied] = useState(false); | ||
| const [emailSent, setEmailSent] = useState(false); |
There was a problem hiding this comment.
Use types
| const [emailSent, setEmailSent] = useState(false); | |
| const [emailSent, setEmailSent] = useState<boolean>(false); |
Purpose
Added Support for email invitation, retaining capability of invite link direct sharing as a fall back.
Related Issues
Related PRs
Checklist
Security checks
Executor ready, No SMTP Configured
part0.mp4
Executor ready,SMTP Configured
part1.mp4
Executor Removed, SMTP Configured
part2.mp4