diff --git a/.changeset/green-elephants-melt.md b/.changeset/green-elephants-melt.md
new file mode 100644
index 00000000000..f2053ca448a
--- /dev/null
+++ b/.changeset/green-elephants-melt.md
@@ -0,0 +1,7 @@
+---
+"@clerk/clerk-js": patch
+"@clerk/localizations": patch
+"@clerk/types": patch
+---
+
+Replace expiration segmented list with dropdown and hide description field in `` component
diff --git a/packages/clerk-js/bundlewatch.config.json b/packages/clerk-js/bundlewatch.config.json
index c942a77329f..d044e48372b 100644
--- a/packages/clerk-js/bundlewatch.config.json
+++ b/packages/clerk-js/bundlewatch.config.json
@@ -1,10 +1,10 @@
{
"files": [
- { "path": "./dist/clerk.js", "maxSize": "610kB" },
+ { "path": "./dist/clerk.js", "maxSize": "610.32kB" },
{ "path": "./dist/clerk.browser.js", "maxSize": "70.2KB" },
{ "path": "./dist/clerk.legacy.browser.js", "maxSize": "113KB" },
{ "path": "./dist/clerk.headless*.js", "maxSize": "53.06KB" },
- { "path": "./dist/ui-common*.js", "maxSize": "108.56KB" },
+ { "path": "./dist/ui-common*.js", "maxSize": "108.75KB" },
{ "path": "./dist/vendors*.js", "maxSize": "40.2KB" },
{ "path": "./dist/coinbase*.js", "maxSize": "38KB" },
{ "path": "./dist/createorganization*.js", "maxSize": "5KB" },
diff --git a/packages/clerk-js/src/ui/components/ApiKeys/ApiKeysTable.tsx b/packages/clerk-js/src/ui/components/ApiKeys/ApiKeysTable.tsx
index 4323d8204eb..fc6917bc9f7 100644
--- a/packages/clerk-js/src/ui/components/ApiKeys/ApiKeysTable.tsx
+++ b/packages/clerk-js/src/ui/components/ApiKeys/ApiKeysTable.tsx
@@ -161,22 +161,23 @@ export const ApiKeysTable = ({
- Created at{' '}
- {apiKey.createdAt.toLocaleDateString(undefined, {
- month: 'short',
- day: '2-digit',
- year: 'numeric',
- })}
-
+ localizationKey={
+ apiKey.expiration
+ ? localizationKeys('apiKeys.createdAndExpirationStatus__expiresOn', {
+ createdDate: apiKey.createdAt,
+ expiresDate: apiKey.expiration,
+ })
+ : localizationKeys('apiKeys.createdAndExpirationStatus__never', {
+ createdDate: apiKey.createdAt,
+ })
+ }
+ />
diff --git a/packages/clerk-js/src/ui/components/ApiKeys/CreateApiKeyForm.tsx b/packages/clerk-js/src/ui/components/ApiKeys/CreateApiKeyForm.tsx
index a5c89ca3790..b5453ef0aec 100644
--- a/packages/clerk-js/src/ui/components/ApiKeys/CreateApiKeyForm.tsx
+++ b/packages/clerk-js/src/ui/components/ApiKeys/CreateApiKeyForm.tsx
@@ -1,62 +1,127 @@
-import React, { useState } from 'react';
+import React, { useMemo, useRef, useState } from 'react';
-import { Box, Button, Col, descriptors, Flex, FormLabel, localizationKeys, Text } from '@/ui/customizables';
+import { useApiKeysContext } from '@/ui/contexts';
+import { Box, Col, descriptors, FormLabel, localizationKeys, Text, useLocalizations } from '@/ui/customizables';
import { useActionContext } from '@/ui/elements/Action/ActionRoot';
import { Form } from '@/ui/elements/Form';
import { FormButtons } from '@/ui/elements/FormButtons';
import { FormContainer } from '@/ui/elements/FormContainer';
-import { SegmentedControl } from '@/ui/elements/SegmentedControl';
+import { Select, SelectButton, SelectOptionList } from '@/ui/elements/Select';
+import { ChevronUpDown } from '@/ui/icons';
import { mqu } from '@/ui/styledSystem';
import { useFormControl } from '@/ui/utils/useFormControl';
-export type OnCreateParams = { name: string; description?: string; expiration: number | undefined };
+const EXPIRATION_VALUES = ['never', '1d', '7d', '30d', '60d', '90d', '180d', '1y'] as const;
+
+type Expiration = (typeof EXPIRATION_VALUES)[number];
+
+type ExpirationOption = {
+ value: Expiration;
+ label: string;
+};
+
+export type OnCreateParams = {
+ name: string;
+ description?: string;
+ secondsUntilExpiration: number | undefined;
+};
interface CreateApiKeyFormProps {
onCreate: (params: OnCreateParams, closeCardFn: () => void) => void;
isSubmitting: boolean;
}
-export type Expiration = 'never' | '30d' | '90d' | 'custom';
+const EXPIRATION_DURATIONS: Record, (date: Date) => void> = {
+ '1d': date => date.setDate(date.getDate() + 1),
+ '7d': date => date.setDate(date.getDate() + 7),
+ '30d': date => date.setDate(date.getDate() + 30),
+ '60d': date => date.setDate(date.getDate() + 60),
+ '90d': date => date.setDate(date.getDate() + 90),
+ '180d': date => date.setDate(date.getDate() + 180),
+ '1y': date => date.setFullYear(date.getFullYear() + 1),
+} as const;
-const getTimeLeftInSeconds = (expirationOption: Expiration, customDate?: string) => {
- if (expirationOption === 'never') {
+const getExpirationLocalizationKey = (expiration: Expiration) => {
+ switch (expiration) {
+ case 'never':
+ return 'apiKeys.formFieldOption__expiration__never';
+ case '1d':
+ return 'apiKeys.formFieldOption__expiration__1d';
+ case '7d':
+ return 'apiKeys.formFieldOption__expiration__7d';
+ case '30d':
+ return 'apiKeys.formFieldOption__expiration__30d';
+ case '60d':
+ return 'apiKeys.formFieldOption__expiration__60d';
+ case '90d':
+ return 'apiKeys.formFieldOption__expiration__90d';
+ case '180d':
+ return 'apiKeys.formFieldOption__expiration__180d';
+ case '1y':
+ return 'apiKeys.formFieldOption__expiration__1y';
+ }
+};
+
+const getTimeLeftInSeconds = (expirationOption?: Expiration): number | undefined => {
+ if (expirationOption === 'never' || !expirationOption) {
return;
}
const now = new Date();
- let future = new Date(now);
+ const future = new Date(now);
- switch (expirationOption) {
- case '30d':
- future.setDate(future.getDate() + 30);
- break;
- case '90d':
- future.setDate(future.getDate() + 90);
- break;
- case 'custom':
- future = new Date(customDate as string);
- break;
- default:
- throw new Error('Invalid expiration option');
- }
-
- const diffInMs = future.getTime() - now.getTime();
- const diffInSecs = Math.floor(diffInMs / 1000);
- return diffInSecs;
+ EXPIRATION_DURATIONS[expirationOption](future);
+ return Math.floor((future.getTime() - now.getTime()) / 1000);
};
-const getMinDate = () => {
- const min = new Date();
- min.setDate(min.getDate() + 1);
- return min.toISOString().split('T')[0];
+interface ExpirationSelectorProps {
+ selectedExpiration: ExpirationOption | null;
+ setSelectedExpiration: (value: ExpirationOption) => void;
+}
+
+const ExpirationSelector: React.FC = ({ selectedExpiration, setSelectedExpiration }) => {
+ const buttonRef = useRef(null);
+ const { t } = useLocalizations();
+
+ const expirationOptions = EXPIRATION_VALUES.map(value => ({
+ value,
+ label: t(localizationKeys(getExpirationLocalizationKey(value))),
+ }));
+
+ return (
+
+ );
};
-export const CreateApiKeyForm = ({ onCreate, isSubmitting }: CreateApiKeyFormProps) => {
- const [showAdvanced, setShowAdvanced] = useState(false);
- const [expiration, setExpiration] = useState('never');
- const createApiKeyFormId = React.useId();
- const segmentedControlId = `${createApiKeyFormId}-segmented-control`;
+export const CreateApiKeyForm: React.FC = ({ onCreate, isSubmitting }) => {
+ const [selectedExpiration, setSelectedExpiration] = useState(null);
const { close: closeCardFn } = useActionContext();
+ const { showDescription = false } = useApiKeysContext();
+ const { t } = useLocalizations();
const nameField = useFormControl('name', '', {
type: 'text',
@@ -72,14 +137,30 @@ export const CreateApiKeyForm = ({ onCreate, isSubmitting }: CreateApiKeyFormPro
isRequired: false,
});
- const expirationDateField = useFormControl('apiKeyExpirationDate', '', {
- type: 'date',
- label: localizationKeys('formFieldLabel__apiKeyExpirationDate'),
- placeholder: localizationKeys('formFieldInputPlaceholder__apiKeyExpirationDate'),
- isRequired: false,
- });
-
const canSubmit = nameField.value.length > 2;
+ const expirationCaption = useMemo(() => {
+ const timeLeftInSeconds = getTimeLeftInSeconds(selectedExpiration?.value);
+
+ if (!selectedExpiration?.value || !timeLeftInSeconds) {
+ return t(localizationKeys('apiKeys.formFieldCaption__expiration__never'));
+ }
+
+ const expirationDate = new Date(Date.now() + timeLeftInSeconds * 1000);
+ return t(
+ localizationKeys('apiKeys.formFieldCaption__expiration__expiresOn', {
+ date: expirationDate.toLocaleString(undefined, {
+ year: 'numeric',
+ month: 'long',
+ day: '2-digit',
+ hour: 'numeric',
+ minute: '2-digit',
+ second: '2-digit',
+ hour12: true,
+ timeZoneName: 'short',
+ }),
+ }),
+ );
+ }, [selectedExpiration?.value]);
const handleSubmit = (e: React.FormEvent) => {
e.preventDefault();
@@ -87,7 +168,7 @@ export const CreateApiKeyForm = ({ onCreate, isSubmitting }: CreateApiKeyFormPro
{
name: nameField.value,
description: descriptionField.value || undefined,
- expiration: getTimeLeftInSeconds(expiration, expirationDateField.value),
+ secondsUntilExpiration: getTimeLeftInSeconds(selectedExpiration?.value),
},
closeCardFn,
);
@@ -100,107 +181,82 @@ export const CreateApiKeyForm = ({ onCreate, isSubmitting }: CreateApiKeyFormPro
elementDescriptor={descriptors.apiKeysCreateForm}
>
- ({
+ gap: t.space.$4,
+ display: 'flex',
+ flexDirection: 'row',
+ [mqu.sm]: {
+ flexDirection: 'column',
+ },
+ })}
>
-
-
- {showAdvanced && (
- <>
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {showDescription && (
+ ({
+ borderTopWidth: t.borderWidths.$normal,
+ borderTopStyle: t.borderStyles.$solid,
+ borderTopColor: t.colors.$neutralAlpha100,
+ paddingTop: t.space.$4,
+ paddingBottom: t.space.$4,
+ })}
+ >
-
-
-
-
-
- setExpiration(value as Expiration)}
- fullWidth
- sx={t => ({ height: t.sizes.$8 })}
- >
-
-
-
-
-
-
- {expiration === 'custom' ? (
-
-
-
- ) : (
-
- )}
-
- >
+
)}
-
-
-
-
+
+
);
diff --git a/packages/clerk-js/src/ui/components/ApiKeys/RevokeAPIKeyConfirmationModal.tsx b/packages/clerk-js/src/ui/components/ApiKeys/RevokeAPIKeyConfirmationModal.tsx
index 3428a6e912b..4653e10c080 100644
--- a/packages/clerk-js/src/ui/components/ApiKeys/RevokeAPIKeyConfirmationModal.tsx
+++ b/packages/clerk-js/src/ui/components/ApiKeys/RevokeAPIKeyConfirmationModal.tsx
@@ -7,7 +7,7 @@ import { Form } from '@/ui/elements/Form';
import { FormButtons } from '@/ui/elements/FormButtons';
import { FormContainer } from '@/ui/elements/FormContainer';
import { Modal } from '@/ui/elements/Modal';
-import { localizationKeys } from '@/ui/localization';
+import { localizationKeys, useLocalizations } from '@/ui/localization';
import { useFormControl } from '@/ui/utils';
type RevokeAPIKeyConfirmationModalProps = {
@@ -31,10 +31,20 @@ export const RevokeAPIKeyConfirmationModal = ({
}: RevokeAPIKeyConfirmationModalProps) => {
const clerk = useClerk();
const { mutate } = useSWRConfig();
+ const { t } = useLocalizations();
+
+ const revokeField = useFormControl('apiKeyRevokeConfirmation', '', {
+ type: 'text',
+ label: `Type "Revoke" to confirm`,
+ placeholder: 'Revoke',
+ isRequired: true,
+ });
+
+ const canSubmit = revokeField.value === t(localizationKeys('apiKeys.revokeConfirmation.confirmationText'));
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
- if (!apiKeyId) return;
+ if (!apiKeyId || !canSubmit) return;
await clerk.apiKeys.revoke({ apiKeyID: apiKeyId });
const cacheKey = { key: 'api-keys', subject };
@@ -43,16 +53,6 @@ export const RevokeAPIKeyConfirmationModal = ({
onClose();
};
- const revokeField = useFormControl('apiKeyRevokeConfirmation', '', {
- type: 'text',
- label: `Type "Revoke" to confirm`,
- placeholder: 'Revoke',
- isRequired: true,
- });
-
- // TODO: Make this locale-aware
- const canSubmit = revokeField.value === 'Revoke';
-
if (!isOpen) {
return null;
}
@@ -77,6 +77,7 @@ export const RevokeAPIKeyConfirmationModal = ({
minHeight: '100%',
height: '100%',
width: '100%',
+ borderRadius: t.radii.$lg,
})
: {},
]}
diff --git a/packages/clerk-js/src/ui/components/OrganizationProfile/OrganizationApiKeysPage.tsx b/packages/clerk-js/src/ui/components/OrganizationProfile/OrganizationApiKeysPage.tsx
index efdef3cb73f..539e6091823 100644
--- a/packages/clerk-js/src/ui/components/OrganizationProfile/OrganizationApiKeysPage.tsx
+++ b/packages/clerk-js/src/ui/components/OrganizationProfile/OrganizationApiKeysPage.tsx
@@ -1,6 +1,6 @@
import { useOrganization } from '@clerk/shared/react';
-import { ApiKeysContext } from '@/ui/contexts';
+import { ApiKeysContext, useOrganizationProfileContext } from '@/ui/contexts';
import { Col, localizationKeys } from '@/ui/customizables';
import { Header } from '@/ui/elements/Header';
import { useUnsafeNavbarContext } from '@/ui/elements/Navbar';
@@ -10,6 +10,7 @@ import { APIKeysPage } from '../ApiKeys/ApiKeys';
export const OrganizationAPIKeysPage = () => {
const { organization } = useOrganization();
const { contentRef } = useUnsafeNavbarContext();
+ const { apiKeysProps } = useOrganizationProfileContext();
if (!organization) {
// We should never reach this point, but we'll return null to make TS happy
@@ -24,7 +25,7 @@ export const OrganizationAPIKeysPage = () => {
textVariant='h2'
/>
-
+
{
const { user } = useUser();
const { contentRef } = useUnsafeNavbarContext();
+ const { apiKeysProps } = useUserProfileContext();
if (!user) {
// We should never reach this point, but we'll return null to make TS happy
@@ -17,14 +18,17 @@ export const APIKeysPage = () => {
}
return (
-
+
-
+
{
const isAccountPageRoot = pages.routes[0].id === USER_PROFILE_NAVBAR_ROUTE_ID.ACCOUNT;
const isSecurityPageRoot = pages.routes[0].id === USER_PROFILE_NAVBAR_ROUTE_ID.SECURITY;
const isBillingPageRoot = pages.routes[0].id === USER_PROFILE_NAVBAR_ROUTE_ID.BILLING;
- const isApiKeysPageRoot = pages.routes[0].id === USER_PROFILE_NAVBAR_ROUTE_ID.API_KEYS;
+ const isAPIKeysPageRoot = pages.routes[0].id === USER_PROFILE_NAVBAR_ROUTE_ID.API_KEYS;
const customPageRoutesWithContents = pages.contents?.map((customPage, index) => {
const shouldFirstCustomItemBeOnRoot = !isAccountPageRoot && !isSecurityPageRoot && index === 0;
@@ -95,7 +95,7 @@ export const UserProfileRoutes = () => {
)}
{apiKeysSettings.enabled && (
-
+
diff --git a/packages/clerk-js/src/ui/customizables/elementDescriptors.ts b/packages/clerk-js/src/ui/customizables/elementDescriptors.ts
index 0b26ff5c4b3..93aeea8689a 100644
--- a/packages/clerk-js/src/ui/customizables/elementDescriptors.ts
+++ b/packages/clerk-js/src/ui/customizables/elementDescriptors.ts
@@ -476,6 +476,7 @@ export const APPEARANCE_KEYS = containsAllElementsConfigKeys([
'apiKeysCreateFormDescriptionInput',
'apiKeysCreateFormExpirationInput',
'apiKeysCreateFormSubmitButton',
+ 'apiKeysCreateFormExpirationCaption',
'apiKeysRevokeModal',
'apiKeysRevokeModalInput',
'apiKeysRevokeModalSubmitButton',
diff --git a/packages/clerk-js/src/ui/utils/timeAgo.ts b/packages/clerk-js/src/ui/utils/timeAgo.ts
index 83dd65e7908..5c7d690346a 100644
--- a/packages/clerk-js/src/ui/utils/timeAgo.ts
+++ b/packages/clerk-js/src/ui/utils/timeAgo.ts
@@ -7,20 +7,20 @@ export function timeAgo(date: Date | string | number) {
if (isNaN(then.getTime())) return '';
const seconds = Math.floor((now.getTime() - then.getTime()) / 1000);
- if (seconds < 60) return localizationKeys('apiKeys.dates.lastUsed__seconds', { seconds });
+ if (seconds < 60) return localizationKeys('apiKeys.lastUsed__seconds', { seconds });
const minutes = Math.floor(seconds / 60);
- if (minutes < 60) return localizationKeys('apiKeys.dates.lastUsed__minutes', { minutes });
+ if (minutes < 60) return localizationKeys('apiKeys.lastUsed__minutes', { minutes });
const hours = Math.floor(minutes / 60);
- if (hours < 24) return localizationKeys('apiKeys.dates.lastUsed__hours', { hours });
+ if (hours < 24) return localizationKeys('apiKeys.lastUsed__hours', { hours });
const days = Math.floor(hours / 24);
- if (days < 30) return localizationKeys('apiKeys.dates.lastUsed__days', { days });
+ if (days < 30) return localizationKeys('apiKeys.lastUsed__days', { days });
const months = Math.floor(days / 30);
- if (months < 12) return localizationKeys('apiKeys.dates.lastUsed__months', { months });
+ if (months < 12) return localizationKeys('apiKeys.lastUsed__months', { months });
const years = Math.floor(months / 12);
- return localizationKeys('apiKeys.dates.lastUsed__years', { years });
+ return localizationKeys('apiKeys.lastUsed__years', { years });
}
diff --git a/packages/localizations/src/de-DE.ts b/packages/localizations/src/de-DE.ts
index 7aa98251c7e..7d5702ac863 100644
--- a/packages/localizations/src/de-DE.ts
+++ b/packages/localizations/src/de-DE.ts
@@ -17,14 +17,6 @@ export const deDE: LocalizationResource = {
apiKeys: {
action__add: 'Neuen API-Key hinzufügen',
action__search: 'Suche',
- dates: {
- lastUsed__days: undefined,
- lastUsed__hours: undefined,
- lastUsed__minutes: undefined,
- lastUsed__months: undefined,
- lastUsed__seconds: undefined,
- lastUsed__years: undefined,
- },
detailsTitle__emptyRow: 'Keine API-Keys gefunden',
formButtonPrimary__add: 'API-Key erstellen',
formHint: 'Geben Sie einen Namen an, um einen API-Key zu erstellen. Sie können ihn jederzeit widerrufen.',
@@ -34,7 +26,22 @@ export const deDE: LocalizationResource = {
formButtonPrimary__revoke: 'API-Key widerrufen',
formHint: 'Sind Sie sicher, dass Sie diesen API-Key löschen wollen?',
formTitle: 'API-Key "{{apiKeyName}}" widerrufen?',
- },
+ confirmationText: 'Widerrufen',
+ },
+ formFieldOption__expiration__1d: undefined,
+ formFieldOption__expiration__7d: undefined,
+ formFieldOption__expiration__30d: undefined,
+ formFieldOption__expiration__60d: undefined,
+ formFieldOption__expiration__90d: undefined,
+ formFieldOption__expiration__180d: undefined,
+ formFieldOption__expiration__1y: undefined,
+ formFieldOption__expiration__never: undefined,
+ lastUsed__seconds: undefined,
+ lastUsed__minutes: undefined,
+ lastUsed__hours: undefined,
+ lastUsed__days: undefined,
+ lastUsed__months: undefined,
+ lastUsed__years: undefined,
},
backButton: 'Zurück',
badge__activePlan: 'Aktiv',
@@ -180,7 +187,6 @@ export const deDE: LocalizationResource = {
formFieldInputPlaceholder__username: 'Benutzername eingeben',
formFieldLabel__apiKeyDescription: 'Beschreibung',
formFieldLabel__apiKeyExpiration: 'Ablaufdatum',
- formFieldLabel__apiKeyExpirationDate: 'Datum auswählen',
formFieldLabel__apiKeyName: 'Name',
formFieldLabel__automaticInvitations: 'Aktivieren Sie automatische Einladungen für diese Domain',
formFieldLabel__backupCode: 'Sicherungscode',
diff --git a/packages/localizations/src/en-US.ts b/packages/localizations/src/en-US.ts
index 979d3d07b74..0729f17a402 100644
--- a/packages/localizations/src/en-US.ts
+++ b/packages/localizations/src/en-US.ts
@@ -5,14 +5,20 @@ export const enUS: LocalizationResource = {
apiKeys: {
action__add: 'Add new key',
action__search: 'Search keys',
- dates: {
- lastUsed__days: '{{days}}d ago',
- lastUsed__hours: '{{hours}}h ago',
- lastUsed__minutes: '{{minutes}}m ago',
- lastUsed__months: '{{months}}mo ago',
- lastUsed__seconds: '{{seconds}}s ago',
- lastUsed__years: '{{years}}y ago',
- },
+ formFieldOption__expiration__1d: '1 Day',
+ formFieldOption__expiration__7d: '7 Days',
+ formFieldOption__expiration__30d: '30 Days',
+ formFieldOption__expiration__60d: '60 Days',
+ formFieldOption__expiration__90d: '90 Days',
+ formFieldOption__expiration__180d: '180 Days',
+ formFieldOption__expiration__1y: '1 Year',
+ formFieldOption__expiration__never: 'Never',
+ lastUsed__seconds: '{{seconds}}s ago',
+ lastUsed__minutes: '{{minutes}}m ago',
+ lastUsed__hours: '{{hours}}h ago',
+ lastUsed__days: '{{days}}d ago',
+ lastUsed__months: '{{months}}mo ago',
+ lastUsed__years: '{{years}}y ago',
detailsTitle__emptyRow: 'No API keys found',
formButtonPrimary__add: 'Create key',
formHint: 'Provide a name to generate a new key. You’ll be able to revoke it anytime.',
@@ -22,7 +28,13 @@ export const enUS: LocalizationResource = {
formButtonPrimary__revoke: 'Revoke key',
formHint: 'Are you sure you want to delete this Secret key?',
formTitle: 'Revoke "{{apiKeyName}}" secret key?',
+ confirmationText: 'Revoke',
},
+ createdAndExpirationStatus__never: "Created {{ createdDate | shortDate('en-US') }} • Never expires",
+ createdAndExpirationStatus__expiresOn:
+ "Created {{ createdDate | shortDate('en-US') }} • Expires {{ expiresDate | longDate('en-US') }}",
+ formFieldCaption__expiration__never: 'This key will never expire',
+ formFieldCaption__expiration__expiresOn: 'Expiring {{ date }}',
},
backButton: 'Back',
badge__activePlan: 'Active',
@@ -149,9 +161,9 @@ export const enUS: LocalizationResource = {
formFieldError__verificationLinkExpired: 'The verification link expired. Please request a new link.',
formFieldHintText__optional: 'Optional',
formFieldHintText__slug: 'A slug is a human-readable ID that must be unique. It’s often used in URLs.',
- formFieldInputPlaceholder__apiKeyDescription: 'Enter your secret key description',
- formFieldInputPlaceholder__apiKeyExpirationDate: 'Enter expiration date',
formFieldInputPlaceholder__apiKeyName: 'Enter your secret key name',
+ formFieldInputPlaceholder__apiKeyExpirationDate: 'Select date',
+ formFieldInputPlaceholder__apiKeyDescription: 'Explain why you’re generating this key',
formFieldInputPlaceholder__backupCode: 'Enter backup code',
formFieldInputPlaceholder__confirmDeletionUserAccount: 'Delete account',
formFieldInputPlaceholder__emailAddress: 'Enter your email address',
@@ -168,8 +180,7 @@ export const enUS: LocalizationResource = {
formFieldInputPlaceholder__username: undefined,
formFieldLabel__apiKeyDescription: 'Description',
formFieldLabel__apiKeyExpiration: 'Expiration',
- formFieldLabel__apiKeyExpirationDate: 'Select date',
- formFieldLabel__apiKeyName: 'Name',
+ formFieldLabel__apiKeyName: 'Secret key name',
formFieldLabel__automaticInvitations: 'Enable automatic invitations for this domain',
formFieldLabel__backupCode: 'Backup code',
formFieldLabel__confirmDeletion: 'Confirmation',
@@ -216,7 +227,7 @@ export const enUS: LocalizationResource = {
},
organizationProfile: {
apiKeysPage: {
- title: 'API Keys',
+ title: 'API keys',
},
badge__automaticInvitation: 'Automatic invitations',
badge__automaticSuggestion: 'Automatic suggestions',
@@ -338,7 +349,7 @@ export const enUS: LocalizationResource = {
},
},
navbar: {
- apiKeys: 'API Keys',
+ apiKeys: 'API keys',
billing: 'Billing',
description: 'Manage your organization.',
general: 'General',
@@ -857,7 +868,7 @@ export const enUS: LocalizationResource = {
},
userProfile: {
apiKeysPage: {
- title: 'API Keys',
+ title: 'API keys',
},
backupCodePage: {
actionLabel__copied: 'Copied!',
diff --git a/packages/types/src/appearance.ts b/packages/types/src/appearance.ts
index f9101dc5bb4..dc647795bee 100644
--- a/packages/types/src/appearance.ts
+++ b/packages/types/src/appearance.ts
@@ -604,6 +604,7 @@ export type ElementsConfig = {
apiKeysCreateFormDescriptionInput: WithOptions;
apiKeysCreateFormExpirationInput: WithOptions;
apiKeysCreateFormSubmitButton: WithOptions;
+ apiKeysCreateFormExpirationCaption: WithOptions;
apiKeysRevokeModal: WithOptions;
apiKeysRevokeModalInput: WithOptions;
apiKeysRevokeModalSubmitButton: WithOptions;
diff --git a/packages/types/src/clerk.ts b/packages/types/src/clerk.ts
index e25115150a9..bf0fd935b27 100644
--- a/packages/types/src/clerk.ts
+++ b/packages/types/src/clerk.ts
@@ -462,22 +462,21 @@ export interface Clerk {
unmountPricingTable: (targetNode: HTMLDivElement) => void;
/**
- * @experimental
* This API is in early access and may change in future releases.
*
* Mount a api keys component at the target element.
+ * @experimental
* @param targetNode Target to mount the APIKeys component.
* @param props Configuration parameters.
*/
mountApiKeys: (targetNode: HTMLDivElement, props?: APIKeysProps) => void;
/**
- * @experimental
* This API is in early access and may change in future releases.
*
* Unmount a api keys component from the target element.
* If there is no component mounted at the target node, results in a noop.
- *
+ * @experimental
* @param targetNode Target node to unmount the ApiKeys component from.
*/
unmountApiKeys: (targetNode: HTMLDivElement) => void;
@@ -1334,6 +1333,12 @@ export type UserProfileProps = RoutingOptions & {
* @experimental
**/
__experimental_startPath?: string;
+ /**
+ * Specify options for the underlying component.
+ * e.g.
+ * @experimental
+ **/
+ apiKeysProps?: APIKeysProps;
};
export type UserProfileModalProps = WithoutRouting;
@@ -1359,6 +1364,12 @@ export type OrganizationProfileProps = RoutingOptions & {
* @experimental
**/
__experimental_startPath?: string;
+ /**
+ * Specify options for the underlying component.
+ * e.g.
+ * @experimental
+ **/
+ apiKeysProps?: APIKeysProps;
};
export type OrganizationProfileModalProps = WithoutRouting;
@@ -1683,6 +1694,11 @@ export type APIKeysProps = {
* prop of ClerkProvider (if one is provided)
*/
appearance?: APIKeysTheme;
+ /**
+ * Whether to show the description field in the API key creation form.
+ * @default false
+ */
+ showDescription?: boolean;
};
export type GetAPIKeysParams = {
diff --git a/packages/types/src/elementIds.ts b/packages/types/src/elementIds.ts
index aa42c78eb5d..22284ef91de 100644
--- a/packages/types/src/elementIds.ts
+++ b/packages/types/src/elementIds.ts
@@ -56,4 +56,4 @@ export type OrganizationPreviewId =
export type CardActionId = 'havingTrouble' | 'alternativeMethods' | 'signUp' | 'signIn' | 'usePasskey' | 'waitlist';
export type MenuId = 'invitation' | 'member' | ProfileSectionId;
-export type SelectId = 'countryCode' | 'role' | 'paymentSource';
+export type SelectId = 'countryCode' | 'role' | 'paymentSource' | 'apiKeyExpiration';
diff --git a/packages/types/src/localization.ts b/packages/types/src/localization.ts
index 37bbcb81e34..16a1002f4b6 100644
--- a/packages/types/src/localization.ts
+++ b/packages/types/src/localization.ts
@@ -106,7 +106,6 @@ export type __internal_LocalizationResource = {
formFieldLabel__apiKeyName: LocalizationValue;
formFieldLabel__apiKeyDescription: LocalizationValue;
formFieldLabel__apiKeyExpiration: LocalizationValue;
- formFieldLabel__apiKeyExpirationDate: LocalizationValue;
formFieldInputPlaceholder__emailAddress: LocalizationValue;
formFieldInputPlaceholder__emailAddresses: LocalizationValue;
formFieldInputPlaceholder__phoneNumber: LocalizationValue;
@@ -1165,15 +1164,26 @@ export type __internal_LocalizationResource = {
formTitle: LocalizationValue<'apiKeyName'>;
formHint: LocalizationValue;
formButtonPrimary__revoke: LocalizationValue;
- };
- dates: {
- lastUsed__seconds: LocalizationValue<'seconds'>;
- lastUsed__minutes: LocalizationValue<'minutes'>;
- lastUsed__hours: LocalizationValue<'hours'>;
- lastUsed__days: LocalizationValue<'days'>;
- lastUsed__months: LocalizationValue<'months'>;
- lastUsed__years: LocalizationValue<'years'>;
- };
+ confirmationText: LocalizationValue;
+ };
+ lastUsed__seconds: LocalizationValue<'seconds'>;
+ lastUsed__minutes: LocalizationValue<'minutes'>;
+ lastUsed__hours: LocalizationValue<'hours'>;
+ lastUsed__days: LocalizationValue<'days'>;
+ lastUsed__months: LocalizationValue<'months'>;
+ lastUsed__years: LocalizationValue<'years'>;
+ formFieldOption__expiration__1d: LocalizationValue;
+ formFieldOption__expiration__7d: LocalizationValue;
+ formFieldOption__expiration__30d: LocalizationValue;
+ formFieldOption__expiration__60d: LocalizationValue;
+ formFieldOption__expiration__90d: LocalizationValue;
+ formFieldOption__expiration__180d: LocalizationValue;
+ formFieldOption__expiration__1y: LocalizationValue;
+ formFieldOption__expiration__never: LocalizationValue;
+ createdAndExpirationStatus__never: LocalizationValue<'createdDate'>;
+ createdAndExpirationStatus__expiresOn: LocalizationValue<'createdDate' | 'expiresDate'>;
+ formFieldCaption__expiration__never: LocalizationValue;
+ formFieldCaption__expiration__expiresOn: LocalizationValue<'date'>;
};
};
|