Skip to content
Open
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
Binary file added auth0-myorganization-js-1.0.0.tgz
Binary file not shown.
4 changes: 2 additions & 2 deletions examples/next-rwa/src/app/member-management/page.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
'use client';

import { OrganizationMemberManagement } from '@auth0/universal-components-react';
// import { OrganizationMemberManagement } from '@auth0/universal-components-react';

export default function MemberManagementPage() {
return (
Expand All @@ -15,7 +15,7 @@ export default function MemberManagementPage() {
</a>{' '}
on how to add Member Management component.
</p>
<OrganizationMemberManagement />
{/* <OrganizationMemberManagement /> */}
</div>
);
}
2 changes: 1 addition & 1 deletion examples/next-rwa/src/components/navigation/side-bar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ export const Sidebar: React.FC = () => {
className="flex items-center gap-3 px-3 py-2 text-sm text-gray-700 hover:text-gray-900 hover:bg-gray-100 rounded-md dark:text-gray-300 dark:hover:text-white dark:hover:bg-gray-800 transition-colors"
>
<Users className="h-4 w-4 flex-shrink-0" />
<span className="truncate">{t('sidebar.members')}</span>
<span className="truncate">{t('sidebar.member-management')}</span>
</Link>
</li>
</ul>
Expand Down
2 changes: 1 addition & 1 deletion examples/next-rwa/src/providers/i18n-provider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ i18n.use(initReactI18next).init({
'sidebar.organization-settings': 'Organization Settings',
'sidebar.domains': 'Domains',
'sidebar.identity-providers': 'Identity Providers',
'sidebar.members': 'Members',
'sidebar.member-management': 'Members',
},
},
},
Expand Down
3 changes: 2 additions & 1 deletion examples/react-spa-npm/src/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
"my-organization": "My Organization",
"organization-management": "Organization Management",
"sso-provider": "SSO Provider",
"domain-management": "Domain Management"
"domain-management": "Domain Management",
"member-management": "Members"
}
}
3 changes: 2 additions & 1 deletion examples/react-spa-npm/src/locales/ja.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
"my-organization": "マイ組織",
"organization-management": "組織管理",
"sso-provider": "SSOプロバイダー",
"domain-management": "ドメイン管理"
"domain-management": "ドメイン管理",
"member-management": "メンバー"
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// import { OrganizationMemberManagement } from '@auth0/universal-components-react/spa';
// import { OrganizationMemberManagement } from '@auth0/universal-components-react';

const MemberManagementPage = () => {
return (
Expand Down
3 changes: 2 additions & 1 deletion examples/react-spa-shadcn/src/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,8 @@
"my-organization": "My Organization",
"organization-management": "Organization Management",
"identity-provider-management": "Identity Provider Management",
"domain-management": "Domain Management"
"domain-management": "Domain Management",
"member-management": "Members"
},
"mfa": {
"title": "Multi-Factor Authentication"
Expand Down
3 changes: 2 additions & 1 deletion examples/react-spa-shadcn/src/locales/ja.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,8 @@
"my-organization": "マイ組織",
"organization-management": "組織管理",
"identity-provider-management": "IDプロバイダー管理",
"domain-management": "ドメイン管理"
"domain-management": "ドメイン管理",
"member-management": "メンバー"
},
"mfa": {
"title": "多要素認証"
Expand Down
6 changes: 1 addition & 5 deletions examples/react-spa-shadcn/src/pages/MemberManagement.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
// import { OrganizationMemberManagement } from '@auth0/universal-components-react/spa';

const MemberManagement = () => {
return (
<div className="p-6">
Expand All @@ -13,9 +11,7 @@ const MemberManagement = () => {
</a>{' '}
on how to add Member Management component.
</p>
<div className="bg-background rounded-lg shadow p-6">
{/* <OrganizationMemberManagement /> */}
</div>
<div className="bg-background rounded-lg shadow p-6"></div>
</div>
);
};
Expand Down
4 changes: 3 additions & 1 deletion examples/scripts/utils/env-writer.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,9 @@ const MYORG_SCOPES = [
"create:my_org:domains",
"update:my_org:domains",
"read:my_org:member_invitations",
"delete:my_org:member_invitations"
"delete:my_org:member_invitations",
"create:my_org:member_invitations",
"read:my_org:member_roles"
]

// My Account API scopes
Expand Down
4 changes: 3 additions & 1 deletion examples/scripts/utils/resource-servers.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,9 @@ export const MYORG_API_SCOPES = [
"delete:my_org:identity_providers_provisioning",
"read:my_org:configuration",
"read:my_org:member_invitations",
"delete:my_org:member_invitations"
"delete:my_org:member_invitations",
"create:my_org:member_invitations",
"read:my_org:member_roles"
]

// My Account API Scopes - desired scopes for MFA management
Expand Down
6 changes: 4 additions & 2 deletions packages/core/src/i18n/translations/en-US.json
Original file line number Diff line number Diff line change
Expand Up @@ -1125,6 +1125,7 @@
"menu_label": "Actions",
"view_details": "View Details",
"copy_url": "Copy Invitation URL",
"copied": "Copied!",
"revoke_and_resend": "Revoke and Resend",
"revoke": "Revoke Invitation"
},
Expand All @@ -1146,7 +1147,8 @@
"submit_button": "Send Invite",
"creating": "Creating...",
"cancel_button": "Cancel",
"success": "Invitation sent to ${email}."
"success": "Invitation has been sent to ${email}.",
"success_bulk": "Invitations have been sent."
},
"details": {
"title": "Invitation Details",
Expand Down Expand Up @@ -1178,7 +1180,7 @@
},
"success": {
"url_copied": "Invitation URL copied to clipboard.",
"invitation_resent": "Invitation resent to ${email}."
"invitation_resent": "Previous invite revoked. A new invitation has been sent to ${email}."
},
"error": {
"fetch_failed": "Failed to load invitations. Please try again.",
Expand Down
6 changes: 4 additions & 2 deletions packages/core/src/i18n/translations/ja.json
Original file line number Diff line number Diff line change
Expand Up @@ -1127,6 +1127,7 @@
"menu_label": "アクション",
"view_details": "詳細を表示",
"copy_url": "招待URLをコピー",
"copied": "コピーしました!",
"revoke_and_resend": "取り消して再送信",
"revoke": "招待を取り消す"
},
Expand All @@ -1148,7 +1149,8 @@
"submit_button": "招待を送信",
"creating": "作成中...",
"cancel_button": "キャンセル",
"success": "${email}に招待を送信しました。"
"success": "${email}に招待が送信されました。",
"success_bulk": "招待が送信されました。"
},
"details": {
"title": "招待の詳細",
Expand Down Expand Up @@ -1180,7 +1182,7 @@
},
"success": {
"url_copied": "招待URLをクリップボードにコピーしました。",
"invitation_resent": "${email}に招待を再送信しました。"
"invitation_resent": "以前の招待が取り消されました。${email}に新しい招待が送信されました。"
},
"error": {
"fetch_failed": "招待の読み込みに失敗しました。もう一度お試しください。",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,5 @@
export const memberManagementQueryKeys = {
all: ['member-management'] as const,
invitations: () => [...memberManagementQueryKeys.all, 'invitations'] as const,
roles: () => [...memberManagementQueryKeys.all, 'roles'] as const,
};
Original file line number Diff line number Diff line change
Expand Up @@ -87,3 +87,13 @@ export type CreateMemberInvitationResponseContent =
* Response content for getting a member invitation.
*/
export type GetMemberInvitationResponseContent = MyOrganization.GetMemberInvitationResponseContent;

/**
* Organization role available for binding to members and invitations.
*/
export type Role = MyOrganization.Role;

/**
* Organization role ID.
*/
export type RoleId = MyOrganization.RoleId;
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,6 @@ export function OrganizationMemberManagementView(props: OrganizationMemberManage
isRevokingInvitation,
isResendingInvitation,
invitationPagination,
invitationFilters,
invitationSortConfig,
availableRoles,
availableProviders,
Expand Down Expand Up @@ -122,8 +121,6 @@ export function OrganizationMemberManagementView(props: OrganizationMemberManage
loading={isFetchingInvitations}
customMessages={customMessages?.invitation}
pagination={invitationPagination}
filters={invitationFilters}
availableRoles={availableRoles}
readOnly={readOnly}
sortConfig={invitationSortConfig}
onSortChange={handleSortChange}
Expand Down Expand Up @@ -223,7 +220,7 @@ export function OrganizationMemberManagement(props: OrganizationMemberManagement
});

return (
<GateKeeper isLoading={memberManagement.isFetchingInvitations} styling={styling}>
<GateKeeper isLoading={memberManagement.isInitialLoading} styling={styling}>
<OrganizationMemberManagementView
{...memberManagement}
styling={styling}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { screen } from '@testing-library/react';
import { screen, act, fireEvent } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { vi, describe, it, expect, afterEach } from 'vitest';

import { OrganizationInvitationDetailsModal } from '@/components/auth0/my-organization/shared/member-management/invitations/invitation-details/organization-invitation-details-modal';
import { renderWithProviders } from '@/tests/utils';
import { renderWithProviders, TestProvider } from '@/tests/utils';
import {
createMockDetailsModalProps,
createMockInvitation,
Expand All @@ -14,6 +14,8 @@ import {
} from '@/tests/utils/__mocks__/my-organization/member-management/invitation.mocks';

describe('OrganizationInvitationDetailsModal', () => {
const invitationUrl = 'https://example.auth0.com/invite?ticket=abc';

afterEach(() => {
vi.clearAllMocks();
});
Expand Down Expand Up @@ -172,7 +174,7 @@ describe('OrganizationInvitationDetailsModal', () => {
describe('invitation URL', () => {
it('should display invitation URL when available', () => {
const invitation = createMockInvitation({
invitation_url: 'https://example.auth0.com/invite?ticket=abc',
invitation_url: invitationUrl,
});

renderWithProviders(
Expand Down Expand Up @@ -266,6 +268,85 @@ describe('OrganizationInvitationDetailsModal', () => {
});
});

describe('copy URL', () => {
it('should call onCopyUrl and show copied state when copy button is clicked', async () => {
const user = userEvent.setup();
const onCopyUrl = vi.fn();
const invitation = createMockInvitation({
invitation_url: invitationUrl,
});

renderWithProviders(
<OrganizationInvitationDetailsModal
{...createMockDetailsModalProps({ invitation, onCopyUrl })}
/>,
);

await user.click(screen.getByRole('button', { name: 'invitation.details.copy_url_button' }));

expect(onCopyUrl).toHaveBeenCalledTimes(1);
expect(onCopyUrl).toHaveBeenCalledWith(invitation);
expect(screen.getByRole('button', { name: 'invitation.details.copied' })).toBeInTheDocument();
});

describe('with fake timers', () => {
afterEach(() => {
vi.useRealTimers();
});

it('should reset copied state after 3 seconds', () => {
vi.useFakeTimers();

const invitation = createMockInvitation({
invitation_url: invitationUrl,
});

renderWithProviders(
<OrganizationInvitationDetailsModal {...createMockDetailsModalProps({ invitation })} />,
);

fireEvent.click(screen.getByRole('button', { name: 'invitation.details.copy_url_button' }));

expect(
screen.getByRole('button', { name: 'invitation.details.copied' }),
).toBeInTheDocument();

act(() => vi.advanceTimersByTime(3000));

expect(
screen.getByRole('button', { name: 'invitation.details.copy_url_button' }),
).toBeInTheDocument();
});

it('should clear the timeout when modal is closed before 3 seconds', () => {
vi.useFakeTimers();

const invitation = createMockInvitation({
invitation_url: invitationUrl,
});

const { rerender } = renderWithProviders(
<OrganizationInvitationDetailsModal {...createMockDetailsModalProps({ invitation })} />,
);

fireEvent.click(screen.getByRole('button', { name: 'invitation.details.copy_url_button' }));

rerender(
<TestProvider>
<OrganizationInvitationDetailsModal
{...createMockDetailsModalProps({ invitation, isOpen: false })}
/>
</TestProvider>,
);

act(() => vi.advanceTimersByTime(3000));

// no state update errors — timeout was cleared cleanly
expect(screen.queryByRole('dialog')).not.toBeInTheDocument();
});
});
});

describe('action callbacks', () => {
it('should call onRevoke when Revoke button is clicked', async () => {
const user = userEvent.setup();
Expand Down
Loading