Skip to content
Draft
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
2 changes: 1 addition & 1 deletion examples/next-rwa/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
"private": true,
"type": "module",
"scripts": {
"dev": "next dev -p 5173",
"dev": "next dev -p 3000",
"build": "next build",
"start": "next start",
"lint": "next lint",
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
"build": "turbo run build",
"build:docs": "turbo run build --filter=auth0-ui-components-docs",
"build:shadcn": "cd packages/react && pnpm registry:build && cp -r public/r ../../docs-site/public/",
"build:mintlify": "cd packages/react && pnpm build:mintlify",
"dev:docs": "turbo run dev --filter=auth0-ui-components-docs",
"test": "turbo run test:coverage",
"test:core": "turbo run test:coverage --filter=./packages/core",
Expand Down
1 change: 1 addition & 0 deletions packages/core/src/auth/auth-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ export interface AuthDetails {
domain?: string | undefined;
authProxyUrl?: string | undefined;
contextInterface?: BasicAuth0ContextInterface | undefined;
offlineMode?: boolean; // For docs - skip API client initialization
}

export interface BaseCoreClientInterface {
Expand Down
27 changes: 27 additions & 0 deletions packages/core/src/auth/core-client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,33 @@ export async function createCoreClient(
);
const auth = initializeAuthDetails(authDetails);

// Skip API clients for docs sites
if (authDetails.offlineMode) {
const baseCoreClient: BaseCoreClientInterface = {
auth,
i18nService,
async getToken() {
return undefined;
},
isProxyMode() {
return false;
},
ensureScopes: async () => {},
};

return {
...baseCoreClient,
myAccountApiClient: undefined,
myOrgApiClient: undefined,
getMyAccountApiClient() {
throw new Error('API clients are not available in docs/demo mode (skipApiClients=true)');
},
getMyOrgApiClient() {
throw new Error('API clients are not available in docs/demo mode (skipApiClients=true)');
},
};
}

const tokenManagerService = createTokenManager(auth);
const { client: myOrgApiClient, setLatestScopes: setOrgScopes } = initializeMyOrgClient(
auth,
Expand Down
3 changes: 2 additions & 1 deletion packages/react/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,8 @@
"test": "vitest run --coverage",
"test:watch": "vitest",
"test:coverage": "vitest run --coverage",
"registry:build": "shadcn build -o ../../docs-site/public/r"
"registry:build": "shadcn build -o ../../docs-site/public/r",
"build:mintlify": "tsup --config ../../tsup.mintlify.config.ts && npx @tailwindcss/cli -i ./src/styles/globals.css -o ./dist/mintlify/styles.css --minify"
},
"peerDependencies": {
"react": "^16.11.0 || ^17 || ^18 || ^19.2.1",
Expand Down
2 changes: 1 addition & 1 deletion packages/react/src/blocks/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,4 @@ export { OrgDetailsEdit } from './my-org/org-management/org-details-edit';
export { SsoProviderTable } from './my-org/idp-management/sso-provider-table';
export { SsoProviderCreate } from './my-org/idp-management/sso-provider-create';
export { SsoProviderEdit } from './my-org/idp-management/sso-provider-edit';
export { DomainTable } from './my-org/domain-management/domain-table';
export { DomainTable, DomainTableView } from './my-org/domain-management';
Original file line number Diff line number Diff line change
@@ -0,0 +1,207 @@
import { getComponentStyles, type Domain } from '@auth0/universal-components-core';
import { useTheme, useTranslator } from '@react/hooks';
import type { DomainTableViewProps } from '@react/types';
import { Plus } from 'lucide-react';
import * as React from 'react';

import { DomainConfigureProvidersModal } from '../../../components/my-org/domain-management/domain-configure/domain-configure-providers-modal';
import { DomainCreateModal } from '../../../components/my-org/domain-management/domain-create/domain-create-modal';
import { DomainDeleteModal } from '../../../components/my-org/domain-management/domain-delete/domain-delete-modal';
import { DomainTableActionsColumn } from '../../../components/my-org/domain-management/domain-table/domain-table-actions-column';
import { DomainVerifyModal } from '../../../components/my-org/domain-management/domain-verify/domain-verify-modal';
import { Badge } from '../../../components/ui/badge';
import { DataTable, type Column } from '../../../components/ui/data-table';
import { Header } from '../../../components/ui/header';
import { getStatusBadgeVariant } from '../../../lib/my-org/domain-management';

/**
* DomainTableView component.
*
* Renders domain table component but requires external state management via the `logic` prop.
* Use this when you need custom Domain Table or want to integrate with your own state.
* @example
* ```tsx
* const customLogic = {
* state: { showCreateModal: false, setShowCreateModal: (show: boolean) => void },
* actions: { handleCreate: (type) => {...}, handleDelete: (id) => {...} },
* domainTableActions: { createAction: async () => {...} }
* };
*
* <DomainTableView logic={customLogic} />
* ```
*/
export function DomainTableView({
customMessages = {},
schema,
hideHeader = false,
readOnly = false,
createAction,
onOpenProvider,
onCreateProvider,
styling = {
variables: { common: {}, light: {}, dark: {} },
classes: {},
},
logic,
}: DomainTableViewProps) {
const { isDarkMode } = useTheme();
const { t } = useTranslator('domain_management', customMessages);

const { state, actions, domainTableActions } = logic;
const {
// State variables and methods
showCreateModal,
showConfigureModal,
showVerifyModal,
showDeleteModal,
verifyError,
selectedDomain,
setShowCreateModal,
setShowConfigureModal,
setShowDeleteModal,
} = state;

const {
// Handlers
handleCreate,
handleVerify,
handleDelete,
handleToggleSwitch,
handleCloseVerifyModal,
handleCreateClick,
handleConfigureClick,
handleVerifyClick,
handleDeleteClick,
} = actions;

const {
domains,
providers,
isCreating,
isVerifying,
isFetching,
isLoadingProviders,
isDeleting,
} = domainTableActions;

const currentStyles = React.useMemo(
() => getComponentStyles(styling, isDarkMode),
[styling, isDarkMode],
);

const columns: Column<Domain>[] = React.useMemo(
() => [
{
type: 'text',
accessorKey: 'domain',
title: t('domain_table.table.columns.domain'),
width: '35%',
render: (domain) => <div className="font-medium">{domain.domain}</div>,
},
{
type: 'text',
accessorKey: 'status',
title: t('domain_table.table.columns.status'),
width: '25%',
render: (domain) => (
<Badge variant={getStatusBadgeVariant(domain.status)} size={'sm'}>
{t(`shared.domain_statuses.${domain.status}`)}
</Badge>
),
},
{
type: 'actions',
title: '',
width: '20%',
render: (domain) => (
<DomainTableActionsColumn
domain={domain}
readOnly={readOnly}
customMessages={customMessages}
onView={handleConfigureClick}
onConfigure={handleConfigureClick}
onVerify={handleVerifyClick}
onDelete={handleDeleteClick}
/>
),
},
],
[t, readOnly, customMessages, handleConfigureClick, handleVerifyClick, handleDeleteClick],
);

return (
<div style={currentStyles.variables}>
{!hideHeader && (
<div className={currentStyles.classes?.['DomainTable-header']}>
<Header
title={t('domain_table.header.title')}
description={t('domain_table.header.description')}
actions={[
{
type: 'button',
label: t('domain_table.header.create_button_text'),
onClick: () => handleCreateClick(),
icon: Plus,
disabled: createAction?.disabled || readOnly || isFetching,
},
]}
/>
</div>
)}

<DataTable
columns={columns}
data={domains}
loading={isFetching}
emptyState={{ title: t('domain_table.table.empty_message') }}
className={currentStyles.classes?.['DomainTable-table']}
/>

<DomainCreateModal
className={currentStyles.classes?.['DomainTable-createModal']}
isOpen={showCreateModal}
isLoading={isCreating}
schema={schema?.create}
onClose={() => setShowCreateModal(false)}
onCreate={handleCreate}
customMessages={customMessages.create}
/>

<DomainConfigureProvidersModal
className={currentStyles.classes?.['DomainTable-configureModal']}
domain={selectedDomain}
providers={providers}
isOpen={showConfigureModal}
isLoading={isLoadingProviders}
isLoadingSwitch={false}
onClose={() => setShowConfigureModal(false)}
onToggleSwitch={handleToggleSwitch}
onOpenProvider={onOpenProvider}
onCreateProvider={onCreateProvider}
customMessages={customMessages.configure}
/>

<DomainVerifyModal
className={currentStyles.classes?.['DomainTable-verifyModal']}
isOpen={showVerifyModal}
isLoading={isVerifying}
domain={selectedDomain}
error={verifyError}
onClose={handleCloseVerifyModal}
onVerify={handleVerify}
onDelete={handleDeleteClick}
customMessages={customMessages.verify}
/>

<DomainDeleteModal
className={currentStyles.classes?.['DomainTable-deleteModal']}
domain={selectedDomain}
isOpen={showDeleteModal}
isLoading={isDeleting}
onClose={() => setShowDeleteModal(false)}
onDelete={handleDelete}
customMessages={customMessages.delete}
/>
</div>
);
}
Loading