Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/rude-zebras-dress.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@asgardeo/react': patch
---

Support sending invite links via email, with direct link sharing as a fallback.
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,11 @@ export interface BaseInviteUserRenderProps {
*/
inviteLinkCopied: boolean;

/**
* Whether the invite email was sent successfully.
*/
isEmailSent: boolean;

/**
* Whether the invite link has been generated (admin flow complete).
*/
Expand Down Expand Up @@ -267,6 +272,7 @@ const BaseInviteUser: FC<BaseInviteUserProps> = ({
const [touchedFields, setTouchedFields] = useState<Record<string, boolean>>({});
const [inviteLink, setInviteLink] = useState<string | undefined>();
const [inviteLinkCopied, setInviteLinkCopied] = useState(false);
const [emailSent, setEmailSent] = useState(false);
Copy link
Contributor

Choose a reason for hiding this comment

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

Use types

Suggested change
const [emailSent, setEmailSent] = useState(false);
const [emailSent, setEmailSent] = useState<boolean>(false);

const [isFormValid, setIsFormValid] = useState(true);

const initializationAttemptedRef: any = useRef(false);
Expand Down Expand Up @@ -437,6 +443,11 @@ const BaseInviteUser: FC<BaseInviteUserProps> = ({
onInviteLinkGenerated?.(inviteLinkValue, response.flowId);
}

// Check if email was sent successfully
if (response.data?.additionalData?.['emailSent'] === 'true') {
setEmailSent(true);
Copy link
Contributor

Choose a reason for hiding this comment

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

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'.

}

// Check for error status
if (response.flowStatus === 'ERROR') {
handleError(response);
Expand Down Expand Up @@ -501,6 +512,7 @@ const BaseInviteUser: FC<BaseInviteUserProps> = ({
setTouchedFields({});
setInviteLink(undefined);
setInviteLinkCopied(false);
setEmailSent(false);
initializationAttemptedRef.current = false;
}, []);

Expand Down Expand Up @@ -624,6 +636,7 @@ const BaseInviteUser: FC<BaseInviteUserProps> = ({
const {title, subtitle} = extractHeadings(components);
const componentsWithoutHeadings: any = filterHeadings(components);
Copy link
Contributor

Choose a reason for hiding this comment

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

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 isInviteGenerated: any = !!inviteLink;
const isEmailSent: boolean = emailSent;
Copy link
Contributor

Choose a reason for hiding this comment

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

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.


// Render props
const renderProps: BaseInviteUserRenderProps = {
Expand All @@ -637,6 +650,7 @@ const BaseInviteUser: FC<BaseInviteUserProps> = ({
handleSubmit,
inviteLink,
inviteLinkCopied,
isEmailSent,
isInviteGenerated,
isLoading,
isValid: isFormValid,
Expand Down Expand Up @@ -695,7 +709,33 @@ const BaseInviteUser: FC<BaseInviteUserProps> = ({
);
}

// Invite link generated - success state
// 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!
Copy link
Contributor

Choose a reason for hiding this comment

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

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.

Comment on lines +715 to +718
Copy link
Contributor

Choose a reason for hiding this comment

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

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.

</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'}}>
Copy link
Contributor

Choose a reason for hiding this comment

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

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.

<Button variant="outline" onClick={resetFlow}>
Invite Another User
</Button>
</div>
</CardPrimitive.Content>
</CardPrimitive>
);
}
Comment on lines +712 to +736
Copy link

Copilot AI Mar 12, 2026

Choose a reason for hiding this comment

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

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).

Copilot uses AI. Check for mistakes.

// Invite link generated but email not sent - show copy link fallback
if (isInviteGenerated && inviteLink) {
return (
<CardPrimitive className={cx(className, styles.card)} variant={variant}>
Expand Down
Loading