diff --git a/auth0-myorganization-js-1.0.0-beta.4.tgz b/auth0-myorganization-js-1.0.0-beta.4.tgz
new file mode 100644
index 000000000..b6675e14d
Binary files /dev/null and b/auth0-myorganization-js-1.0.0-beta.4.tgz differ
diff --git a/packages/core/package.json b/packages/core/package.json
index 31755887c..d7e3ae79d 100644
--- a/packages/core/package.json
+++ b/packages/core/package.json
@@ -52,7 +52,7 @@
},
"dependencies": {
"@auth0/myaccount-js": "1.0.0-beta.0",
- "@auth0/myorganization-js": "1.0.0",
+ "@auth0/myorganization-js": "file:../../auth0-myorganization-js-1.0.0-beta.4.tgz",
"zod": "^3.22.4"
}
}
diff --git a/packages/core/src/services/my-organization/index.ts b/packages/core/src/services/my-organization/index.ts
index deb30e8e6..5e1816d52 100644
--- a/packages/core/src/services/my-organization/index.ts
+++ b/packages/core/src/services/my-organization/index.ts
@@ -7,4 +7,5 @@
export * from './organization-management';
export * from './idp-management';
export * from './domain-management';
+export * from './member-management/member-management-types';
export * from './config';
diff --git a/packages/core/src/services/my-organization/member-management/member-management-types.ts b/packages/core/src/services/my-organization/member-management/member-management-types.ts
new file mode 100644
index 000000000..7c7561743
--- /dev/null
+++ b/packages/core/src/services/my-organization/member-management/member-management-types.ts
@@ -0,0 +1,111 @@
+/**
+ * Member management type definitions for organization member and invitation operations.
+ * @module member-management-types
+ * @internal
+ */
+import type { MyOrganization } from '@auth0/myorganization-js';
+
+/**
+ * Organization member ID type.
+ */
+export type OrgMemberId = MyOrganization.OrgMemberId;
+
+/**
+ * Organization member entity.
+ */
+export type OrgMember = MyOrganization.OrgMember;
+
+/**
+ * Organization member role.
+ */
+export type OrgMemberRole = MyOrganization.OrgMemberRole;
+
+/**
+ * Organization member role ID.
+ */
+export type OrgMemberRoleId = MyOrganization.OrgMemberRoleId;
+
+/**
+ * Response content for listing organization members.
+ */
+export type ListOrganizationMembersResponseContent =
+ MyOrganization.ListOrganizationMembersResponseContent;
+
+/**
+ * Response content for getting a single organization member.
+ */
+export type GetOrganizationMemberResponseContent =
+ MyOrganization.GetOrganizationMemberResponseContent;
+
+/**
+ * Request parameters for listing organization members.
+ */
+export type ListOrganizationMembersRequestParameters =
+ MyOrganization.ListOrganizationMembersRequestParameters;
+
+/**
+ * Response content for getting organization member roles.
+ */
+export type GetOrganizationMemberRolesResponseContent =
+ MyOrganization.GetOrganizationMemberRolesResponseContent;
+
+/**
+ * Request content for assigning a role to an organization member.
+ */
+export type AssignOrganizationMemberRoleRequestContent =
+ MyOrganization.AssignOrganizationMemberRoleRequestContent;
+
+/**
+ * Response content for assigning a role to an organization member.
+ */
+export type AssignOrganizationMemberRoleResponseContent =
+ MyOrganization.AssignOrganizationMemberRoleResponseContent;
+
+/**
+ * Invitation ID type.
+ */
+export type InvitationId = MyOrganization.InvitationId;
+
+/**
+ * Member invitation entity.
+ */
+export type MemberInvitation = MyOrganization.MemberInvitation;
+
+/**
+ * Member invitation invitee details.
+ */
+export type MemberInvitationInvitee = MyOrganization.MemberInvitationInvitee;
+
+/**
+ * Member invitation inviter details.
+ */
+export type MemberInvitationInviter = MyOrganization.MemberInvitationInviter;
+
+/**
+ * Response content for listing member invitations.
+ */
+export type ListMembersInvitationsResponseContent =
+ MyOrganization.ListMembersInvitationsResponseContent;
+
+/**
+ * Request parameters for listing member invitations.
+ */
+export type ListMemberInvitationsRequestParameters =
+ MyOrganization.ListMemberInvitationsRequestParameters;
+
+/**
+ * Request content for creating a member invitation.
+ */
+export type CreateMemberInvitationRequestContent =
+ MyOrganization.CreateMemberInvitationRequestContent;
+
+/**
+ * Response content for creating a member invitation.
+ */
+export type CreateMemberInvitationResponseContent =
+ MyOrganization.CreateMemberInvitationResponseContent;
+
+/**
+ * Response content for getting a member invitation.
+ */
+export type GetMemberInvitationResponseContent = MyOrganization.GetMemberInvitationResponseContent;
diff --git a/packages/react/src/components/auth0/my-organization/shared/member-management/invitations/invitation-details/organization-invitation-details-modal.tsx b/packages/react/src/components/auth0/my-organization/shared/member-management/invitations/invitation-details/organization-invitation-details-modal.tsx
new file mode 100644
index 000000000..c0bcb91ad
--- /dev/null
+++ b/packages/react/src/components/auth0/my-organization/shared/member-management/invitations/invitation-details/organization-invitation-details-modal.tsx
@@ -0,0 +1,236 @@
+/**
+ * Organization invitation details modal component.
+ * @module organization-invitation-details-modal
+ */
+
+import { Link } from 'lucide-react';
+import * as React from 'react';
+
+import { CopyableTextField } from '@/components/auth0/shared/copyable-text-field';
+import { Badge } from '@/components/ui/badge';
+import { Button } from '@/components/ui/button';
+import {
+ Dialog,
+ DialogContent,
+ DialogDescription,
+ DialogFooter,
+ DialogHeader,
+ DialogTitle,
+} from '@/components/ui/dialog';
+import { Label } from '@/components/ui/label';
+import { Spinner } from '@/components/ui/spinner';
+import { TextField } from '@/components/ui/text-field';
+import { TextFieldGroup } from '@/components/ui/text-field-group';
+import { useTranslator } from '@/hooks/shared/use-translator';
+import { getInvitationStatus } from '@/lib/utils/my-organization/member-management/member-management-utils';
+import type {
+ InvitationStatus,
+ OrganizationInvitationDetailsModalProps,
+} from '@/types/my-organization/member-management/organization-invitation-table-types';
+
+export type { OrganizationInvitationDetailsModalProps };
+
+/**
+ * Returns the badge variant for a given invitation status.
+ * @param status - The invitation status.
+ * @returns The badge variant string.
+ */
+function getStatusBadgeVariant(status: InvitationStatus): 'warning' | 'destructive' {
+ return status === 'pending' ? 'warning' : 'destructive';
+}
+
+/**
+ * Modal for viewing invitation details with revoke and resend actions.
+ * @param props - The component props.
+ * @param props.invitation - The invitation to display.
+ * @param props.isOpen - Whether the modal is open.
+ * @param props.isRevoking - Whether a revoke action is in progress.
+ * @param props.isResending - Whether a resend action is in progress.
+ * @param props.customMessages - Custom translation messages.
+ * @param props.availableRoles - Available roles for display.
+ * @param props.availableProviders - Available providers for display.
+ * @param props.readOnly - Whether in read-only mode.
+ * @param props.onClose - Callback when modal is closed.
+ * @param props.onCopyUrl - Callback when copy URL is clicked.
+ * @param props.onRevoke - Callback when revoke is clicked.
+ * @param props.onResend - Callback when revoke and resend is clicked.
+ * @param props.className - Optional CSS class name.
+ * @returns The modal component.
+ */
+export function OrganizationInvitationDetailsModal({
+ invitation,
+ isOpen,
+ isRevoking = false,
+ isResending = false,
+ customMessages = {},
+ availableRoles = [],
+ availableProviders = [],
+ readOnly = false,
+ onClose,
+ onCopyUrl,
+ onRevoke,
+ onResend,
+ className,
+}: OrganizationInvitationDetailsModalProps): React.JSX.Element {
+ const { t } = useTranslator('member_management', customMessages);
+
+ const status = invitation ? getInvitationStatus(invitation) : 'pending';
+ const isPending = status === 'pending';
+ const isActionInProgress = isRevoking || isResending;
+
+ const roleNames = React.useMemo(() => {
+ if (!invitation?.roles || invitation.roles.length === 0) return [];
+ return invitation.roles
+ .map((roleId) => {
+ const role = availableRoles.find((r) => r.id === roleId);
+ return role?.name ?? roleId;
+ })
+ .filter(Boolean);
+ }, [invitation?.roles, availableRoles]);
+
+ const providerName = React.useMemo(() => {
+ if (!invitation?.identity_provider_id) return null;
+ const provider = availableProviders.find((p) => p.id === invitation.identity_provider_id);
+ return provider?.name ?? invitation.identity_provider_id;
+ }, [invitation?.identity_provider_id, availableProviders]);
+
+ const handleCopyUrl = React.useCallback(() => {
+ if (invitation) {
+ onCopyUrl?.(invitation);
+ }
+ }, [invitation, onCopyUrl]);
+
+ const handleRevoke = React.useCallback(() => {
+ if (invitation) {
+ onRevoke?.(invitation);
+ }
+ }, [invitation, onRevoke]);
+
+ const handleResend = React.useCallback(() => {
+ if (invitation) {
+ onResend?.(invitation);
+ }
+ }, [invitation, onResend]);
+
+ return (
+
+ );
+}
diff --git a/packages/react/src/components/auth0/my-organization/shared/member-management/invitations/invitation-revoke/organization-invitation-revoke-modal.tsx b/packages/react/src/components/auth0/my-organization/shared/member-management/invitations/invitation-revoke/organization-invitation-revoke-modal.tsx
new file mode 100644
index 000000000..25a926278
--- /dev/null
+++ b/packages/react/src/components/auth0/my-organization/shared/member-management/invitations/invitation-revoke/organization-invitation-revoke-modal.tsx
@@ -0,0 +1,84 @@
+/**
+ * Organization invitation revoke modal component.
+ * @module organization-invitation-revoke-modal
+ */
+
+import * as React from 'react';
+
+import { Button } from '@/components/ui/button';
+import {
+ Dialog,
+ DialogContent,
+ DialogDescription,
+ DialogFooter,
+ DialogHeader,
+ DialogTitle,
+} from '@/components/ui/dialog';
+import { Spinner } from '@/components/ui/spinner';
+import { useTranslator } from '@/hooks/shared/use-translator';
+import type { OrganizationInvitationRevokeModalProps } from '@/types/my-organization/member-management/organization-invitation-table-types';
+
+export type { OrganizationInvitationRevokeModalProps };
+
+/**
+ * Modal for confirming invitation revocation or revoke and resend.
+ * @param props - The component props.
+ * @param props.invitation - The invitation to revoke.
+ * @param props.isOpen - Whether the modal is open.
+ * @param props.isLoading - Whether the action is in progress.
+ * @param props.isRevokeAndResend - Whether this is a revoke and resend action.
+ * @param props.customMessages - Custom translation messages.
+ * @param props.onClose - Callback when modal is closed.
+ * @param props.onConfirm - Callback when action is confirmed.
+ * @param props.className - Optional CSS class name.
+ * @returns The modal component.
+ */
+export function OrganizationInvitationRevokeModal({
+ invitation,
+ isOpen,
+ isLoading = false,
+ isRevokeAndResend = false,
+ customMessages = {},
+ onClose,
+ onConfirm,
+ className,
+}: OrganizationInvitationRevokeModalProps): React.JSX.Element {
+ const { t } = useTranslator('member_management', customMessages);
+
+ const namespace = isRevokeAndResend ? 'invitation.revoke_resend' : 'invitation.revoke';
+
+ const handleConfirm = React.useCallback(() => {
+ if (invitation) {
+ onConfirm(invitation);
+ }
+ }, [invitation, onConfirm]);
+
+ return (
+
+ );
+}
diff --git a/packages/react/src/components/auth0/my-organization/shared/member-management/invitations/invitation-table/organization-invitation-table-actions-column.tsx b/packages/react/src/components/auth0/my-organization/shared/member-management/invitations/invitation-table/organization-invitation-table-actions-column.tsx
new file mode 100644
index 000000000..dae2372b9
--- /dev/null
+++ b/packages/react/src/components/auth0/my-organization/shared/member-management/invitations/invitation-table/organization-invitation-table-actions-column.tsx
@@ -0,0 +1,110 @@
+/**
+ * Organization invitation table row actions dropdown.
+ * @module organization-invitation-table-actions-column
+ * @internal
+ */
+
+import { MoreHorizontal, Eye, Copy, RefreshCcw, Trash2 } from 'lucide-react';
+import * as React from 'react';
+
+import {
+ DropdownMenu,
+ DropdownMenuContent,
+ DropdownMenuItem,
+ DropdownMenuTrigger,
+ DropdownMenuPortal,
+ DropdownMenuSeparator,
+} from '@/components/ui/dropdown-menu';
+import { useTranslator } from '@/hooks/shared/use-translator';
+import { getInvitationStatus } from '@/lib/utils/my-organization/member-management/member-management-utils';
+import type { OrganizationInvitationTableActionsColumnProps } from '@/types/my-organization/member-management/organization-invitation-table-types';
+
+/**
+ * OrganizationInvitationTableActionsColumn Component
+ * Handles the actions column for Invitation table with dropdown menu.
+ * @param props - Component props.
+ * @param props.invitation - The invitation to show actions for.
+ * @param props.customMessages - Custom translation messages to override defaults.
+ * @param props.readOnly - Whether the component is in read-only mode.
+ * @param props.onViewDetails - Callback fired when view details action is triggered.
+ * @param props.onCopyUrl - Callback fired when copy URL action is triggered.
+ * @param props.onRevokeAndResend - Callback fired when revoke and resend action is triggered.
+ * @param props.onRevoke - Callback fired when revoke action is triggered.
+ * @returns JSX element.
+ */
+export function OrganizationInvitationTableActionsColumn({
+ invitation,
+ customMessages = {},
+ readOnly = false,
+ onViewDetails,
+ onCopyUrl,
+ onRevokeAndResend,
+ onRevoke,
+}: OrganizationInvitationTableActionsColumnProps): React.JSX.Element {
+ const { t } = useTranslator('member_management', customMessages);
+ const status = getInvitationStatus(invitation);
+ const isPending = status === 'pending';
+
+ const handleViewDetails = React.useCallback(() => {
+ onViewDetails?.(invitation);
+ }, [invitation, onViewDetails]);
+
+ const handleCopyUrl = React.useCallback(() => {
+ onCopyUrl?.(invitation);
+ }, [invitation, onCopyUrl]);
+
+ const handleRevokeAndResend = React.useCallback(() => {
+ onRevokeAndResend?.(invitation);
+ }, [invitation, onRevokeAndResend]);
+
+ const handleRevoke = React.useCallback(() => {
+ onRevoke?.(invitation);
+ }, [invitation, onRevoke]);
+
+ return (
+
+
+
+
+
+
+
+ {/* View Details - always available */}
+
+
+ {t('invitation.actions.view_details')}
+
+
+ {/* Copy URL - only for pending invitations with URL */}
+ {isPending && invitation.invitation_url && (
+
+
+ {t('invitation.actions.copy_url')}
+
+ )}
+
+ {!readOnly && (
+ <>
+
+
+ {t('invitation.actions.revoke_and_resend')}
+
+
+
+
+ {t('invitation.actions.revoke')}
+
+ >
+ )}
+
+
+
+
+ );
+}
diff --git a/packages/react/src/components/auth0/my-organization/shared/member-management/invitations/invitation-table/organization-invitation-table.tsx b/packages/react/src/components/auth0/my-organization/shared/member-management/invitations/invitation-table/organization-invitation-table.tsx
new file mode 100644
index 000000000..f68ba2ef0
--- /dev/null
+++ b/packages/react/src/components/auth0/my-organization/shared/member-management/invitations/invitation-table/organization-invitation-table.tsx
@@ -0,0 +1,188 @@
+/**
+ * Organization invitation table component.
+ * @module organization-invitation-table
+ * @internal
+ */
+
+import type { MemberInvitation } from '@auth0/universal-components-core';
+import * as React from 'react';
+
+import { OrganizationInvitationTableActionsColumn } from './organization-invitation-table-actions-column';
+
+import { SearchFilter } from '@/components/auth0/my-organization/shared/member-management/shared/search-filter/search-filter';
+import { DataPagination } from '@/components/auth0/shared/data-pagination';
+import { DataTable, type Column } from '@/components/auth0/shared/data-table';
+import { Badge } from '@/components/ui/badge';
+import { useTranslator } from '@/hooks/shared/use-translator';
+import { cn } from '@/lib/utils';
+import { getInvitationStatus } from '@/lib/utils/my-organization/member-management/member-management-utils';
+import type { OrganizationInvitationTableProps } from '@/types/my-organization/member-management/organization-invitation-table-types';
+
+/**
+ * Organization invitation table component.
+ * Displays invitations with search, filtering, and pagination.
+ * @param props - The component props.
+ * @param props.invitations - The list of invitations to display.
+ * @param props.loading - Whether the table is loading.
+ * @param props.customMessages - Custom translation messages.
+ * @param props.pagination - Pagination state.
+ * @param props.filters - Current filter state.
+ * @param props.availableRoles - Available roles for filtering.
+ * @param props.readOnly - Whether the component is in read-only mode.
+ * @param props.onView - Callback when viewing invitation details.
+ * @param props.onCopyUrl - Callback when copying invitation URL.
+ * @param props.onRevokeAndResend - Callback when revoking and resending invitation.
+ * @param props.onRevoke - Callback when revoking invitation.
+ * @param props.onPageChange - Callback when page changes.
+ * @param props.onPageSizeChange - Callback when page size changes.
+ * @param props.onRoleFilterChange - Callback when role filter changes.
+ * @param props.className - Optional CSS class name.
+ * @returns The invitation table component.
+ */
+export function OrganizationInvitationTable({
+ invitations,
+ loading = false,
+ customMessages = {},
+ pagination,
+ filters,
+ availableRoles,
+ readOnly = false,
+ sortConfig,
+ onSortChange,
+ onView,
+ onCopyUrl,
+ onRevokeAndResend,
+ onRevoke,
+ onNextPage,
+ onPreviousPage,
+ onPageSizeChange,
+ onRoleFilterChange,
+ className,
+}: OrganizationInvitationTableProps): React.JSX.Element {
+ const { t } = useTranslator('member_management', customMessages);
+
+ const renderDate = (_invitation: MemberInvitation, value: string | number | Date) => (
+
+ {new Date(value).toLocaleString(undefined, {
+ year: 'numeric',
+ month: 'short',
+ day: 'numeric',
+ hour: 'numeric',
+ minute: '2-digit',
+ })}
+
+ );
+
+ const columns: Column[] = React.useMemo(
+ () => [
+ {
+ type: 'text',
+ accessorKey: 'invitee',
+ title: t('invitation.table.columns.email'),
+ enableSorting: false,
+ render: (invitation) => (
+ {invitation.invitee?.email}
+ ),
+ },
+ {
+ type: 'text',
+ accessorKey: 'organization_id',
+ title: t('invitation.table.columns.status'),
+ enableSorting: false,
+ render: (invitation) => {
+ const status = getInvitationStatus(invitation);
+ return (
+
+ {status === 'pending'
+ ? t('invitation.table.status_pending')
+ : t('invitation.table.status_expired')}
+
+ );
+ },
+ },
+ {
+ type: 'date',
+ accessorKey: 'created_at',
+ title: t('invitation.table.columns.created_at'),
+ enableSorting: true,
+ format: 'medium',
+ render: renderDate,
+ },
+ {
+ type: 'date',
+ accessorKey: 'expires_at',
+ title: t('invitation.table.columns.expires_at'),
+ enableSorting: false,
+ format: 'medium',
+ render: renderDate,
+ },
+ {
+ type: 'text',
+ accessorKey: 'inviter',
+ title: t('invitation.table.columns.inviter'),
+ enableSorting: false,
+ render: (invitation) => (
+ {invitation.inviter?.name ?? '-'}
+ ),
+ },
+ {
+ type: 'actions',
+ title: '',
+ enableSorting: false,
+ render: (invitation) => (
+
+ ),
+ },
+ ],
+ [t, customMessages, readOnly, onView, onCopyUrl, onRevokeAndResend, onRevoke],
+ );
+
+ return (
+
+
+
+
+
+ {invitations.length > 0 && (
+
+
+
+ )}
+
+ );
+}
diff --git a/packages/react/src/components/auth0/my-organization/shared/member-management/shared/invitation-create/organization-invitation-create-modal.tsx b/packages/react/src/components/auth0/my-organization/shared/member-management/shared/invitation-create/organization-invitation-create-modal.tsx
new file mode 100644
index 000000000..339821bf8
--- /dev/null
+++ b/packages/react/src/components/auth0/my-organization/shared/member-management/shared/invitation-create/organization-invitation-create-modal.tsx
@@ -0,0 +1,278 @@
+/**
+ * Organization invitation create modal component.
+ * @module organization-invitation-create-modal
+ */
+
+import { createInvitationCreateSchema } from '@auth0/universal-components-core';
+import * as React from 'react';
+
+import { Button } from '@/components/ui/button';
+import { Combobox } from '@/components/ui/combobox';
+import {
+ Dialog,
+ DialogContent,
+ DialogDescription,
+ DialogFooter,
+ DialogHeader,
+ DialogTitle,
+} from '@/components/ui/dialog';
+import { Label } from '@/components/ui/label';
+import {
+ Select,
+ SelectContent,
+ SelectItem,
+ SelectTrigger,
+ SelectValue,
+} from '@/components/ui/select';
+import { TextFieldGroup } from '@/components/ui/text-field-group';
+import type { ChipItem } from '@/components/ui/text-field-group';
+import { useTranslator } from '@/hooks/shared/use-translator';
+import type { OrganizationInvitationCreateModalProps } from '@/types/my-organization/member-management/organization-invitation-table-types';
+
+export type { OrganizationInvitationCreateModalProps };
+
+/**
+ * Modal for creating a new invitation.
+ * Supports multiple email addresses, role selection, and provider selection.
+ * Validation rules can be overridden via the `schema` prop.
+ *
+ * @param props - The component props.
+ * @param props.isOpen - Whether the modal is open.
+ * @param props.isLoading - Whether the form is loading.
+ * @param props.customMessages - Custom translation messages.
+ * @param props.availableRoles - Available roles for selection.
+ * @param props.availableProviders - Available identity providers.
+ * @param props.inviterName - Name of the person sending the invitation.
+ * @param props.schema - Schema overrides for validation (email regex, maxEmails, error messages).
+ * @param props.onClose - Callback when modal is closed.
+ * @param props.onCreate - Callback when invitation is created.
+ * @param props.className - Optional CSS class name.
+ * @returns The modal component.
+ */
+export function OrganizationInvitationCreateModal({
+ isOpen,
+ isLoading = false,
+ customMessages = {},
+ availableRoles = [],
+ availableProviders = [],
+ inviterName,
+ schema,
+ onClose,
+ onCreate,
+ className,
+}: OrganizationInvitationCreateModalProps): React.JSX.Element {
+ const { t } = useTranslator('member_management', customMessages);
+
+ const validationConfig = React.useMemo(
+ () => createInvitationCreateSchema(schema, t('invitation.create.email_invalid_error')),
+ [schema, t],
+ );
+
+ const [emailInput, setEmailInput] = React.useState('');
+ const [emailChips, setEmailChips] = React.useState([]);
+ const [selectedRoles, setSelectedRoles] = React.useState([]);
+ const [selectedProvider, setSelectedProvider] = React.useState();
+ const [emailError, setEmailError] = React.useState();
+
+ const handleEmailInputChange = React.useCallback((e: React.ChangeEvent) => {
+ setEmailInput(e.target.value);
+ setEmailError(undefined);
+ }, []);
+
+ const hasInvalidChips = React.useMemo(
+ () => emailChips.some((chip) => chip.variant === 'destructive'),
+ [emailChips],
+ );
+
+ const handleEmailChipAdd = React.useCallback(
+ (value: string) => {
+ const trimmedEmail = value.trim().replace(/,/g, '');
+
+ if (!trimmedEmail) return;
+
+ if (emailChips.length >= validationConfig.maxEmails) {
+ setEmailError(t('invitation.create.email_limit_error'));
+ return;
+ }
+
+ if (emailChips.some((chip) => chip.value === trimmedEmail)) {
+ setEmailError(t('invitation.create.email_duplicate_error'));
+ return;
+ }
+
+ const result = validationConfig.emailSchema.safeParse(trimmedEmail);
+ if (!result.success) {
+ setEmailChips((prev) => [
+ ...prev,
+ { label: trimmedEmail, value: trimmedEmail, variant: 'destructive' },
+ ]);
+ setEmailInput('');
+ setEmailError(t('invitation.create.email_invalid_error'));
+ return;
+ }
+
+ setEmailChips((prev) => [...prev, { label: trimmedEmail, value: trimmedEmail }]);
+ setEmailInput('');
+ setEmailError(undefined);
+ },
+ [emailChips, validationConfig, t],
+ );
+
+ const handleEmailChipRemove = React.useCallback((value: string) => {
+ setEmailChips((prev) => {
+ const updated = prev.filter((chip) => chip.value !== value);
+ if (!updated.some((chip) => chip.variant === 'destructive')) {
+ setEmailError(undefined);
+ }
+ return updated;
+ });
+ }, []);
+
+ const handleRoleChange = React.useCallback((value: string | string[]) => {
+ setSelectedRoles(Array.isArray(value) ? value : value ? [value] : []);
+ }, []);
+
+ const handleProviderChange = React.useCallback((value: string) => {
+ setSelectedProvider(value || undefined);
+ }, []);
+
+ const handleSubmit = React.useCallback(
+ (e: React.FormEvent) => {
+ e.preventDefault();
+ const finalEmails = emailChips
+ .filter((chip) => chip.variant !== 'destructive')
+ .map((chip) => chip.value);
+
+ if (emailInput.trim()) {
+ const trimmedEmail = emailInput.trim();
+ const result = validationConfig.emailSchema.safeParse(trimmedEmail);
+ if (result.success && !finalEmails.includes(trimmedEmail)) {
+ finalEmails.push(trimmedEmail);
+ }
+ }
+
+ if (finalEmails.length === 0) {
+ setEmailError(t('invitation.create.email_required_error'));
+ return;
+ }
+
+ onCreate({
+ invitees: finalEmails.map((email) => ({
+ email,
+ roles: selectedRoles.length > 0 ? selectedRoles : undefined,
+ })),
+ identity_provider_id: selectedProvider,
+ ...(inviterName && { inviter: { name: inviterName } }),
+ });
+ },
+ [
+ emailChips,
+ emailInput,
+ validationConfig,
+ selectedRoles,
+ selectedProvider,
+ inviterName,
+ onCreate,
+ t,
+ ],
+ );
+
+ const handleClose = React.useCallback(() => {
+ setEmailInput('');
+ setEmailChips([]);
+ setSelectedRoles([]);
+ setSelectedProvider(undefined);
+ setEmailError(undefined);
+ onClose();
+ }, [onClose]);
+
+ const canSubmit = React.useMemo(
+ () =>
+ !hasInvalidChips &&
+ (emailChips.length > 0 ||
+ (emailInput.trim() !== '' &&
+ validationConfig.emailSchema.safeParse(emailInput.trim()).success)),
+ [emailChips.length, emailInput, validationConfig, hasInvalidChips],
+ );
+
+ const roleOptions = React.useMemo(
+ () => availableRoles.map((role) => ({ label: role.name, value: role.id })),
+ [availableRoles],
+ );
+
+ return (
+
+ );
+}
diff --git a/packages/react/src/components/auth0/my-organization/shared/member-management/shared/search-filter/search-filter.tsx b/packages/react/src/components/auth0/my-organization/shared/member-management/shared/search-filter/search-filter.tsx
new file mode 100644
index 000000000..62da9a66f
--- /dev/null
+++ b/packages/react/src/components/auth0/my-organization/shared/member-management/shared/search-filter/search-filter.tsx
@@ -0,0 +1,86 @@
+/**
+ * Search and filter component for invitations.
+ * @module search-filter
+ * @internal
+ */
+
+import { X } from 'lucide-react';
+import * as React from 'react';
+
+import { Button } from '@/components/ui/button';
+import {
+ Select,
+ SelectContent,
+ SelectItem,
+ SelectTrigger,
+ SelectValue,
+} from '@/components/ui/select';
+import { useTranslator } from '@/hooks/shared/use-translator';
+import type { SearchFilterProps } from '@/types/my-organization/member-management/organization-invitation-table-types';
+
+/**
+ * Filter bar for invitation table.
+ * Shows a right-aligned role filter dropdown with a reset button.
+ * @param props - The component props.
+ * @param props.filters - Current filter state.
+ * @param props.availableRoles - Available roles for filtering.
+ * @param props.customMessages - Custom translation messages.
+ * @param props.className - Optional CSS class name.
+ * @param props.onRoleFilterChange - Callback fired when role filter changes.
+ * @returns The filter bar component.
+ */
+export function SearchFilter({
+ filters,
+ availableRoles = [],
+ customMessages = {},
+ className,
+ onRoleFilterChange,
+}: SearchFilterProps): React.JSX.Element | null {
+ const { t } = useTranslator('member_management', customMessages);
+
+ const handleRoleFilterChange = React.useCallback(
+ (value: string) => {
+ onRoleFilterChange?.(value === 'all' ? undefined : value);
+ },
+ [onRoleFilterChange],
+ );
+
+ const handleReset = React.useCallback(() => {
+ onRoleFilterChange?.(undefined);
+ }, [onRoleFilterChange]);
+
+ const hasActiveFilter = !!filters?.roleId;
+
+ if (availableRoles.length === 0) {
+ return null;
+ }
+
+ return (
+
+
+
+
+
+ );
+}
diff --git a/packages/react/src/lib/utils/my-organization/member-management/member-management-utils.ts b/packages/react/src/lib/utils/my-organization/member-management/member-management-utils.ts
new file mode 100644
index 000000000..8db62fd1e
--- /dev/null
+++ b/packages/react/src/lib/utils/my-organization/member-management/member-management-utils.ts
@@ -0,0 +1,20 @@
+/**
+ * Member management utility functions.
+ * @module member-management-utils
+ * @internal
+ */
+
+import type { MemberInvitation } from '@auth0/universal-components-core';
+
+import type { InvitationStatus } from '@/types/my-organization/member-management/organization-invitation-table-types';
+
+/**
+ * Determines the status of an invitation based on `expires_at`.
+ * @param invitation - The invitation to check.
+ * @returns The invitation status.
+ */
+export function getInvitationStatus(invitation: MemberInvitation): InvitationStatus {
+ const isExpired = invitation.expires_at && new Date(invitation.expires_at) < new Date();
+
+ return isExpired ? 'expired' : 'pending';
+}
diff --git a/packages/react/src/types/index.ts b/packages/react/src/types/index.ts
index b46dc7304..852a42fec 100644
--- a/packages/react/src/types/index.ts
+++ b/packages/react/src/types/index.ts
@@ -28,3 +28,5 @@ export * from './my-organization/idp-management/sso-provisioning/provisioning-to
export * from './my-organization/idp-management/sso-provisioning/sso-provisioning-tab-types';
export * from './my-organization/organization-management/organization-details-edit-types';
export * from './my-organization/organization-management/organization-details-types';
+export * from './my-organization/member-management/organization-invitation-table-types';
+export * from './my-organization/member-management/organization-member-management-types';
diff --git a/packages/react/src/types/my-organization/member-management/organization-invitation-table-types.ts b/packages/react/src/types/my-organization/member-management/organization-invitation-table-types.ts
new file mode 100644
index 000000000..103e91c8c
--- /dev/null
+++ b/packages/react/src/types/my-organization/member-management/organization-invitation-table-types.ts
@@ -0,0 +1,172 @@
+/**
+ * Organization invitation table types.
+ * @module organization-invitation-table-types
+ */
+
+import type {
+ SharedComponentProps,
+ ComponentAction,
+ MemberInvitation,
+ InvitationCreateSchemas,
+ OrganizationInvitationTabMessages,
+} from '@auth0/universal-components-core';
+
+/** Invitation status. */
+export type InvitationStatus = 'pending' | 'expired';
+
+/** Role option for invitation. */
+export interface RoleOption {
+ id: string;
+ name: string;
+ description?: string;
+}
+
+/** Identity provider option for invitation. */
+export interface IdentityProviderOption {
+ id: string;
+ name: string;
+ type?: string;
+}
+
+/** Input for creating invitation(s). Supports bulk invite via invitees array. */
+export interface CreateInvitationInput {
+ invitees: Array<{
+ email: string;
+ roles?: string[];
+ }>;
+ inviter?: {
+ name?: string;
+ };
+ identity_provider_id?: string;
+ /** Time to live in seconds */
+ ttl_sec?: number;
+}
+
+/** Pagination state for invitation table (checkpoint-based). */
+export interface InvitationPaginationState {
+ pageSize: number;
+ currentPage: number;
+ totalItems?: number;
+ hasNextPage: boolean;
+ hasPreviousPage: boolean;
+}
+
+/** Sort configuration for invitation table. */
+export interface InvitationSortConfig {
+ key: string | null;
+ direction: 'asc' | 'desc';
+}
+
+/** Filter state for invitation table. */
+export interface InvitationFilterState {
+ searchQuery?: string;
+ roleId?: string;
+}
+
+/** CSS classes for OrganizationInvitationTab. */
+export interface OrganizationInvitationTabClasses {
+ 'OrganizationInvitationTab-root'?: string;
+ 'OrganizationInvitationTab-table'?: string;
+ 'OrganizationInvitationTab-createModal'?: string;
+ 'OrganizationInvitationTab-detailsModal'?: string;
+ 'OrganizationInvitationTab-revokeModal'?: string;
+ 'OrganizationInvitationTab-revokeResendModal'?: string;
+ 'OrganizationInvitationTab-searchInput'?: string;
+ 'OrganizationInvitationTab-filterDropdown'?: string;
+ 'OrganizationInvitationTab-pagination'?: string;
+}
+
+/** Props for OrganizationInvitationTab component. */
+export interface OrganizationInvitationTabProps
+ extends SharedComponentProps<
+ OrganizationInvitationTabMessages,
+ OrganizationInvitationTabClasses
+ > {
+ createAction?: ComponentAction;
+ revokeAction?: ComponentAction;
+}
+
+/** Props for OrganizationInvitationTableActionsColumn component. */
+export interface OrganizationInvitationTableActionsColumnProps {
+ invitation: MemberInvitation;
+ customMessages?: Partial;
+ readOnly?: boolean;
+ onViewDetails?: (invitation: MemberInvitation) => void;
+ onCopyUrl?: (invitation: MemberInvitation) => void;
+ onRevokeAndResend?: (invitation: MemberInvitation) => void;
+ onRevoke?: (invitation: MemberInvitation) => void;
+}
+
+/** Props for OrganizationInvitationTable component. */
+export interface OrganizationInvitationTableProps {
+ invitations: MemberInvitation[];
+ loading?: boolean;
+ customMessages?: Partial;
+ pagination: InvitationPaginationState;
+ filters?: InvitationFilterState;
+ sortConfig?: InvitationSortConfig;
+ availableRoles?: RoleOption[];
+ readOnly?: boolean;
+ onView?: (invitation: MemberInvitation) => void;
+ onCopyUrl?: (invitation: MemberInvitation) => void;
+ onRevokeAndResend?: (invitation: MemberInvitation) => void;
+ onRevoke?: (invitation: MemberInvitation) => void;
+ onNextPage?: () => void;
+ onPreviousPage?: () => void;
+ onPageSizeChange?: (pageSize: number) => void;
+ onSortChange?: (sortConfig: InvitationSortConfig) => void;
+ onRoleFilterChange?: (roleId: string | undefined) => void;
+ className?: string;
+}
+
+/** Props for SearchFilter component. */
+export interface SearchFilterProps {
+ filters?: InvitationFilterState;
+ availableRoles?: RoleOption[];
+ customMessages?: Partial;
+ className?: string;
+ onRoleFilterChange?: (roleId: string | undefined) => void;
+}
+
+/** Props for OrganizationInvitationCreateModal component. */
+export interface OrganizationInvitationCreateModalProps {
+ isOpen: boolean;
+ isLoading?: boolean;
+ customMessages?: Partial;
+ availableRoles?: RoleOption[];
+ availableProviders?: IdentityProviderOption[];
+ inviterName?: string;
+ schema?: InvitationCreateSchemas;
+ onClose: () => void;
+ onCreate: (data: CreateInvitationInput) => void;
+ className?: string;
+}
+
+/** Props for OrganizationInvitationDetailsModal component. */
+export interface OrganizationInvitationDetailsModalProps {
+ invitation: MemberInvitation | null;
+ isOpen: boolean;
+ isRevoking?: boolean;
+ isResending?: boolean;
+ customMessages?: Partial;
+ availableRoles?: RoleOption[];
+ availableProviders?: IdentityProviderOption[];
+ readOnly?: boolean;
+ onClose: () => void;
+ onCopyUrl?: (invitation: MemberInvitation) => void;
+ onRevoke?: (invitation?: MemberInvitation) => void;
+ onResend?: (invitation?: MemberInvitation) => void;
+ className?: string;
+}
+
+/** Props for OrganizationInvitationRevokeModal component. */
+export interface OrganizationInvitationRevokeModalProps {
+ invitation: MemberInvitation | null;
+ isOpen: boolean;
+ isLoading?: boolean;
+ isRevokeAndResend?: boolean;
+ customMessages?: Partial;
+ onClose: () => void;
+ onConfirm: (invitation: MemberInvitation) => void;
+ className?: string;
+}
diff --git a/packages/react/src/types/my-organization/member-management/organization-member-management-types.ts b/packages/react/src/types/my-organization/member-management/organization-member-management-types.ts
new file mode 100644
index 000000000..aa12341c7
--- /dev/null
+++ b/packages/react/src/types/my-organization/member-management/organization-member-management-types.ts
@@ -0,0 +1,132 @@
+/**
+ * Organization member management types.
+ * @module organization-member-management-types
+ */
+
+import type {
+ ComponentAction,
+ SharedComponentProps,
+ MemberInvitation,
+ OrganizationMemberManagementMessages,
+} from '@auth0/universal-components-core';
+import type { UseMutationResult, UseQueryResult } from '@tanstack/react-query';
+
+import type {
+ CreateInvitationInput,
+ IdentityProviderOption,
+ InvitationFilterState,
+ InvitationPaginationState,
+ InvitationSortConfig,
+ OrganizationInvitationTabClasses,
+ RoleOption,
+} from './organization-invitation-table-types';
+
+export type ActiveTab = 'members' | 'invitations';
+
+export interface TableQueryParams {
+ pageSize: number;
+ fromToken: string | undefined;
+ sortConfig: TSort;
+ filters: TFilter;
+}
+
+export interface UseMemberManagementServiceOptions {
+ customMessages?: OrganizationMemberManagementMessages;
+ activeTab: ActiveTab;
+ createInvitationAction?: ComponentAction;
+ revokeInvitationAction?: ComponentAction;
+ resendInvitationAction?: ComponentAction;
+ invitationParams: TableQueryParams;
+}
+
+export interface MemberManagementServiceResult {
+ providersQuery: UseQueryResult;
+ invitationsQuery: UseQueryResult<{
+ invitations: MemberInvitation[];
+ next: string | null;
+ total: number | undefined;
+ }>;
+ createInvitationMutation: UseMutationResult<
+ MemberInvitation | undefined,
+ Error,
+ CreateInvitationInput
+ >;
+ revokeInvitationMutation: UseMutationResult;
+ resendInvitationMutation: UseMutationResult<
+ MemberInvitation | undefined,
+ Error,
+ MemberInvitation
+ >;
+ fetchInvitationDetails: (invitationId: string) => Promise;
+}
+
+export interface UseOrganizationMemberManagementOptions {
+ customMessages?: OrganizationMemberManagementMessages;
+ readOnly?: boolean;
+ /** Action hooks for invitation creation (onBefore/onAfter) */
+ createInvitationAction?: ComponentAction;
+ /** Action hooks for invitation revocation (onBefore/onAfter) */
+ revokeInvitationAction?: ComponentAction;
+ /** Action hooks for invitation revoke-and-resend (onBefore/onAfter) */
+ resendInvitationAction?: ComponentAction;
+}
+
+/** Discriminated union for member management modal state. */
+export type MemberManagementModalState =
+ | { type: null }
+ | { type: 'create' }
+ | { type: 'details'; invitation: MemberInvitation }
+ | { type: 'revoke'; invitation: MemberInvitation }
+ | { type: 'revokeResend'; invitation: MemberInvitation };
+
+export interface UseOrganizationMemberManagementResult {
+ activeTab: ActiveTab;
+ isLoading: boolean;
+ availableRoles: RoleOption[];
+ availableProviders: IdentityProviderOption[];
+
+ invitations: MemberInvitation[];
+ isFetchingInvitations: boolean;
+ isCreatingInvitation: boolean;
+ isRevokingInvitation: boolean;
+ isResendingInvitation: boolean;
+ invitationPagination: InvitationPaginationState;
+ invitationFilters: InvitationFilterState;
+ invitationSortConfig: InvitationSortConfig;
+ modalState: MemberManagementModalState;
+
+ setActiveTab: (tab: ActiveTab) => void;
+ openModal: (state: MemberManagementModalState) => void;
+ closeModal: () => void;
+ handleCreateSubmit: (data: CreateInvitationInput) => void;
+ handleRevokeConfirm: () => void;
+ handleRevokeResendConfirm: () => void;
+ handleCopyUrl: (invitation: MemberInvitation) => Promise;
+ handleNextPage: () => void;
+ handlePreviousPage: () => void;
+ handlePageSizeChange: (pageSize: number) => void;
+ handleSortChange: (sortConfig: InvitationSortConfig) => void;
+ handleRoleFilterChange: (roleId: string | undefined) => void;
+}
+
+/** CSS classes for OrganizationMemberManagement. */
+export interface OrganizationMemberManagementClasses extends OrganizationInvitationTabClasses {
+ 'OrganizationMemberManagement-root'?: string;
+ 'OrganizationMemberManagement-header'?: string;
+ 'OrganizationMemberManagement-tabs'?: string;
+}
+
+/** Props for OrganizationMemberManagement component. */
+export interface OrganizationMemberManagementProps
+ extends SharedComponentProps<
+ OrganizationMemberManagementMessages,
+ OrganizationMemberManagementClasses
+ > {
+ hideHeader?: boolean;
+ /** Action hooks for invitation creation (onBefore/onAfter) */
+ createInvitationAction?: ComponentAction;
+ /** Action hooks for invitation revocation (onBefore/onAfter) */
+ revokeInvitationAction?: ComponentAction;
+ /** Action hooks for invitation revoke-and-resend (onBefore/onAfter) */
+ resendInvitationAction?: ComponentAction;
+}
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 34cd1533f..8d6842bd3 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -548,8 +548,8 @@ importers:
specifier: 1.0.0-beta.0
version: 1.0.0-beta.0
'@auth0/myorganization-js':
- specifier: 1.0.0
- version: 1.0.0
+ specifier: file:../../auth0-myorganization-js-1.0.0-beta.4.tgz
+ version: file:auth0-myorganization-js-1.0.0-beta.4.tgz
zod:
specifier: ^3.22.4
version: 3.25.76
@@ -733,9 +733,10 @@ packages:
resolution: {integrity: sha512-slj0RtNfieNk1BC1ERrCQw65qMUVKU5qacbTc8BFH8R316CUpsOhZ2MIiV9l3VEkaqY1hmCZm03+ZI6ym+3PZg==}
engines: {node: '>=18.0.0'}
- '@auth0/myorganization-js@1.0.0':
- resolution: {integrity: sha512-mYGa95tFj3xgUKKVSi4B95Yt4FPppFfbtmWM9fvXUEgwSgmLHre6vHLwcnsXTPB/rF7ATpAtMMIsWq1N5h9Y4w==}
- engines: {node: '>=20.0.0'}
+ '@auth0/myorganization-js@file:auth0-myorganization-js-1.0.0-beta.4.tgz':
+ resolution: {integrity: sha512-yTZbJ0K75WsCGsb9bXBGTs32WLXEXqyTuM9lgmYST8uSag2ILfJdYZPQZkvyKM5v+7Sk5B9vz0mg3tNfv8SFNg==, tarball: file:auth0-myorganization-js-1.0.0-beta.4.tgz}
+ version: 1.0.0-beta.4
+ engines: {node: '>=18.0.0'}
'@auth0/nextjs-auth0@4.16.0':
resolution: {integrity: sha512-QTQdK+/YL68J7b1tSdvJTT16+r+Dxwy/m0kwl73CEy/QsYCpHE1sCZYgQ4UaFZb5jHQ7d4R2JUkHN5k2fKQ4zg==}
@@ -7363,7 +7364,7 @@ snapshots:
'@auth0/myaccount-js@1.0.0-beta.0': {}
- '@auth0/myorganization-js@1.0.0':
+ '@auth0/myorganization-js@file:auth0-myorganization-js-1.0.0-beta.4.tgz':
dependencies:
'@auth0/auth0-auth-js': 1.5.0