From cbee2b1a32488a8f5762d681eeab784335fabba2 Mon Sep 17 00:00:00 2001 From: wobsoriano Date: Mon, 5 May 2025 14:43:55 -0700 Subject: [PATCH 001/116] chore: set initial files for new component --- packages/clerk-js/sandbox/app.ts | 5 ++++ packages/clerk-js/sandbox/template.html | 8 +++++++ packages/clerk-js/src/core/clerk.ts | 24 +++++++++++++++++++ .../ManageApiKeys/ManageApiKeys.tsx | 17 +++++++++++++ .../src/ui/components/ManageApiKeys/index.tsx | 1 + .../ui/contexts/ClerkUIComponentsContext.tsx | 9 ++++++- .../ui/contexts/components/ManageApiKeys.ts | 20 ++++++++++++++++ .../src/ui/contexts/components/index.ts | 1 + .../clerk-js/src/ui/lazyModules/components.ts | 6 +++++ packages/clerk-js/src/ui/types.ts | 13 ++++++++-- packages/types/src/appearance.ts | 5 ++++ packages/types/src/clerk.ts | 21 ++++++++++++++++ 12 files changed, 127 insertions(+), 3 deletions(-) create mode 100644 packages/clerk-js/src/ui/components/ManageApiKeys/ManageApiKeys.tsx create mode 100644 packages/clerk-js/src/ui/components/ManageApiKeys/index.tsx create mode 100644 packages/clerk-js/src/ui/contexts/components/ManageApiKeys.ts diff --git a/packages/clerk-js/sandbox/app.ts b/packages/clerk-js/sandbox/app.ts index e64bc222ee6..4fde3c997bf 100644 --- a/packages/clerk-js/sandbox/app.ts +++ b/packages/clerk-js/sandbox/app.ts @@ -33,6 +33,7 @@ const AVAILABLE_COMPONENTS = [ 'organizationSwitcher', 'waitlist', 'pricingTable', + 'manageApiKeys', ] as const; const COMPONENT_PROPS_NAMESPACE = 'clerk-js-sandbox'; @@ -91,6 +92,7 @@ const componentControls: Record<(typeof AVAILABLE_COMPONENTS)[number], Component organizationSwitcher: buildComponentControls('organizationSwitcher'), waitlist: buildComponentControls('waitlist'), pricingTable: buildComponentControls('pricingTable'), + manageApiKeys: buildComponentControls('manageApiKeys'), }; declare global { @@ -310,6 +312,9 @@ void (async () => { '/pricing-table': () => { Clerk.mountPricingTable(app, componentControls.pricingTable.getProps() ?? {}); }, + '/manage-api-keys': () => { + Clerk.mountManageApiKeys(app, componentControls.manageApiKeys.getProps() ?? {}); + }, '/open-sign-in': () => { mountOpenSignInButton(app, componentControls.signIn.getProps() ?? {}); }, diff --git a/packages/clerk-js/sandbox/template.html b/packages/clerk-js/sandbox/template.html index cd4207e24c4..4d13f32196a 100644 --- a/packages/clerk-js/sandbox/template.html +++ b/packages/clerk-js/sandbox/template.html @@ -260,6 +260,14 @@ PricingTable +
  • + + Manage API Keys + +
  • { + this.assertComponentsReady(this.#componentControls); + void this.#componentControls.ensureMounted({ preloadHint: 'ManageApiKeys' }).then(controls => + controls.mountComponent({ + name: 'ManageApiKeys', + appearanceKey: 'manageApiKeys', + node, + props, + }), + ); + + this.telemetry?.record(eventPrebuiltComponentMounted('ManageApiKeys', props)); + }; + + public unmountManageApiKeys = (node: HTMLDivElement): void => { + this.assertComponentsReady(this.#componentControls); + void this.#componentControls.ensureMounted().then(controls => + controls.unmountComponent({ + node, + }), + ); + }; + /** * `setActive` can be used to set the active session and/or organization. */ diff --git a/packages/clerk-js/src/ui/components/ManageApiKeys/ManageApiKeys.tsx b/packages/clerk-js/src/ui/components/ManageApiKeys/ManageApiKeys.tsx new file mode 100644 index 00000000000..4cd2679481b --- /dev/null +++ b/packages/clerk-js/src/ui/components/ManageApiKeys/ManageApiKeys.tsx @@ -0,0 +1,17 @@ +import { useEffect } from 'react'; + +import { useManageApiKeysContext } from '../../contexts'; + +export const ManageApiKeys = () => { + const ctx = useManageApiKeysContext(); + + useEffect(() => { + console.log(ctx); + }, [ctx]); + + return ( +
    +

    Manage API Keys

    +
    + ); +}; diff --git a/packages/clerk-js/src/ui/components/ManageApiKeys/index.tsx b/packages/clerk-js/src/ui/components/ManageApiKeys/index.tsx new file mode 100644 index 00000000000..1cd1c4ab7df --- /dev/null +++ b/packages/clerk-js/src/ui/components/ManageApiKeys/index.tsx @@ -0,0 +1 @@ +export * from './ManageApiKeys'; diff --git a/packages/clerk-js/src/ui/contexts/ClerkUIComponentsContext.tsx b/packages/clerk-js/src/ui/contexts/ClerkUIComponentsContext.tsx index 975e6078ffc..f788f2f3412 100644 --- a/packages/clerk-js/src/ui/contexts/ClerkUIComponentsContext.tsx +++ b/packages/clerk-js/src/ui/contexts/ClerkUIComponentsContext.tsx @@ -1,10 +1,11 @@ -import type { PricingTableProps, UserButtonProps, WaitlistProps } from '@clerk/types'; +import type { ManageApiKeysProps, PricingTableProps, UserButtonProps, WaitlistProps } from '@clerk/types'; import type { ReactNode } from 'react'; import type { AvailableComponentName, AvailableComponentProps } from '../types'; import { CreateOrganizationContext, GoogleOneTapContext, + ManageApiKeysContext, OrganizationListContext, OrganizationProfileContext, OrganizationSwitcherContext, @@ -88,6 +89,12 @@ export function ComponentContextProvider({ ); + case 'ManageApiKeys': + return ( + + {children} + + ); default: throw new Error(`Unknown component context: ${componentName}`); diff --git a/packages/clerk-js/src/ui/contexts/components/ManageApiKeys.ts b/packages/clerk-js/src/ui/contexts/components/ManageApiKeys.ts new file mode 100644 index 00000000000..e3ee7fcc172 --- /dev/null +++ b/packages/clerk-js/src/ui/contexts/components/ManageApiKeys.ts @@ -0,0 +1,20 @@ +import { createContext, useContext } from 'react'; + +import type { ManageApiKeysCtx } from '../../types'; + +export const ManageApiKeysContext = createContext(null); + +export const useManageApiKeysContext = () => { + const context = useContext(ManageApiKeysContext); + + if (!context || context.componentName !== 'ManageApiKeys') { + throw new Error('Clerk: useManageApiKeysContext called outside ManageApiKeys.'); + } + + const { componentName, ...ctx } = context; + + return { + ...ctx, + componentName, + }; +}; diff --git a/packages/clerk-js/src/ui/contexts/components/index.ts b/packages/clerk-js/src/ui/contexts/components/index.ts index 5bb1e062d6f..6f9e5a2abf2 100644 --- a/packages/clerk-js/src/ui/contexts/components/index.ts +++ b/packages/clerk-js/src/ui/contexts/components/index.ts @@ -15,3 +15,4 @@ export * from './PricingTable'; export * from './Checkout'; export * from './Statements'; export * from './Plans'; +export * from './ManageApiKeys'; diff --git a/packages/clerk-js/src/ui/lazyModules/components.ts b/packages/clerk-js/src/ui/lazyModules/components.ts index eebba1e0a69..f3fbf423d07 100644 --- a/packages/clerk-js/src/ui/lazyModules/components.ts +++ b/packages/clerk-js/src/ui/lazyModules/components.ts @@ -21,6 +21,7 @@ const componentImportPaths = { Checkout: () => import(/* webpackChunkName: "checkout" */ '../components/Checkout'), SessionTasks: () => import(/* webpackChunkName: "sessionTasks" */ '../components/SessionTasks'), PlanDetails: () => import(/* webpackChunkName: "planDetails" */ '../components/Plans'), + ManageApiKeys: () => import(/* webpackChunkName: "manageApiKeys" */ '../components/ManageApiKeys'), } as const; export const SignIn = lazy(() => componentImportPaths.SignIn().then(module => ({ default: module.SignIn }))); @@ -96,6 +97,10 @@ export const PricingTable = lazy(() => componentImportPaths.PricingTable().then(module => ({ default: module.PricingTable })), ); +export const ManageApiKeys = lazy(() => + componentImportPaths.ManageApiKeys().then(module => ({ default: module.ManageApiKeys })), +); + export const Checkout = lazy(() => componentImportPaths.Checkout().then(module => ({ default: module.Checkout }))); export const PlanDetails = lazy(() => @@ -133,6 +138,7 @@ export const ClerkComponents = { PricingTable, Checkout, PlanDetails, + ManageApiKeys, }; export type ClerkComponentName = keyof typeof ClerkComponents; diff --git a/packages/clerk-js/src/ui/types.ts b/packages/clerk-js/src/ui/types.ts index 8108dc44ce6..346d3bf8f35 100644 --- a/packages/clerk-js/src/ui/types.ts +++ b/packages/clerk-js/src/ui/types.ts @@ -4,6 +4,7 @@ import type { __internal_UserVerificationProps, CreateOrganizationProps, GoogleOneTapProps, + ManageApiKeysProps, NewSubscriptionRedirectUrl, OrganizationListProps, OrganizationProfileProps, @@ -47,7 +48,8 @@ export type AvailableComponentProps = | PricingTableProps | __internal_CheckoutProps | __internal_UserVerificationProps - | __internal_PlanDetailsProps; + | __internal_PlanDetailsProps + | ManageApiKeysProps; type ComponentMode = 'modal' | 'mounted'; @@ -114,6 +116,11 @@ export type PricingTableCtx = PricingTableProps & { mode?: ComponentMode; }; +export type ManageApiKeysCtx = ManageApiKeysProps & { + componentName: 'ManageApiKeys'; + mode?: ComponentMode; +}; + export type CheckoutCtx = __internal_CheckoutProps & { componentName: 'Checkout'; } & NewSubscriptionRedirectUrl; @@ -136,5 +143,7 @@ export type AvailableComponentCtx = | GoogleOneTapCtx | WaitlistCtx | PricingTableCtx - | CheckoutCtx; + | CheckoutCtx + | ManageApiKeysCtx; + export type AvailableComponentName = AvailableComponentCtx['componentName']; diff --git a/packages/types/src/appearance.ts b/packages/types/src/appearance.ts index 2b2bb4c1d35..c7a1656ee04 100644 --- a/packages/types/src/appearance.ts +++ b/packages/types/src/appearance.ts @@ -833,6 +833,7 @@ export type WaitlistTheme = Theme; export type PricingTableTheme = Theme; export type CheckoutTheme = Theme; export type PlanDetailTheme = Theme; +export type ManageApiKeysTheme = Theme; type GlobalAppearanceOptions = { /** @@ -897,4 +898,8 @@ export type Appearance = T & * Theme overrides that only apply to the `` component */ checkout?: T; + /** + * Theme overrides that only apply to the `` component + */ + manageApiKeys?: T; }; diff --git a/packages/types/src/clerk.ts b/packages/types/src/clerk.ts index c0826d84779..f3e34aad53f 100644 --- a/packages/types/src/clerk.ts +++ b/packages/types/src/clerk.ts @@ -2,6 +2,7 @@ import type { Appearance, CheckoutTheme, CreateOrganizationTheme, + ManageApiKeysTheme, OrganizationListTheme, OrganizationProfileTheme, OrganizationSwitcherTheme, @@ -458,6 +459,21 @@ export interface Clerk { */ unmountPricingTable: (targetNode: HTMLDivElement) => void; + /** + * Mount a manage api keys component at the target element. + * @param targetNode Target to mount the ManageApiKeys component. + * @param props Configuration parameters. + */ + mountManageApiKeys: (targetNode: HTMLDivElement, props?: ManageApiKeysProps) => void; + + /** + * Unmount a manage api keys component from the target element. + * If there is no component mounted at the target node, results in a noop. + * + * @param targetNode Target node to unmount the ManageApiKeys component from. + */ + unmountManageApiKeys: (targetNode: HTMLDivElement) => void; + /** * Register a listener that triggers a callback each time important Clerk resources are changed. * Allows to hook up at different steps in the sign up, sign in processes. @@ -1621,6 +1637,11 @@ type PortalRoot = HTMLElement | null | undefined; export type PricingTableProps = PricingTableBaseProps & PricingTableDefaultProps; +export type ManageApiKeysProps = { + [key: string]: unknown; + appearance?: ManageApiKeysTheme; +}; + export type __internal_CheckoutProps = { appearance?: CheckoutTheme; planId?: string; From c5c254d21cdc6e46603a52fecb2fb44344e2123d Mon Sep 17 00:00:00 2001 From: wobsoriano Date: Mon, 5 May 2025 18:51:42 -0700 Subject: [PATCH 002/116] chore: add resources and test methods --- packages/clerk-js/src/core/clerk.ts | 6 ++ .../clerk-js/src/core/resources/ApiKey.ts | 61 +++++++++++++++++++ .../clerk-js/src/core/resources/internal.ts | 1 + .../ManageApiKeys/ManageApiKeys.tsx | 12 ++-- packages/types/src/apiKey.ts | 18 ++++++ packages/types/src/clerk.ts | 10 ++- packages/types/src/index.ts | 1 + packages/types/src/json.ts | 17 ++++++ 8 files changed, 121 insertions(+), 5 deletions(-) create mode 100644 packages/clerk-js/src/core/resources/ApiKey.ts create mode 100644 packages/types/src/apiKey.ts diff --git a/packages/clerk-js/src/core/clerk.ts b/packages/clerk-js/src/core/clerk.ts index dcb39875495..0f82740d358 100644 --- a/packages/clerk-js/src/core/clerk.ts +++ b/packages/clerk-js/src/core/clerk.ts @@ -19,6 +19,7 @@ import type { __internal_ComponentNavigationContext, __internal_PlanDetailsProps, __internal_UserVerificationModalProps, + ApiKeyResource, AuthenticateWithCoinbaseWalletParams, AuthenticateWithGoogleOneTapParams, AuthenticateWithMetamaskParams, @@ -134,6 +135,7 @@ import { createFapiClient } from './fapiClient'; import { createClientFromJwt } from './jwt-client'; import { CommerceBilling } from './modules/commerce'; import { + ApiKey, BaseResource, Client, EmailLinkError, @@ -1061,6 +1063,10 @@ export class Clerk implements ClerkInterface { ); }; + public getApiKeys = async (): Promise => { + return ApiKey.getAll(); + }; + /** * `setActive` can be used to set the active session and/or organization. */ diff --git a/packages/clerk-js/src/core/resources/ApiKey.ts b/packages/clerk-js/src/core/resources/ApiKey.ts new file mode 100644 index 00000000000..5dbab295239 --- /dev/null +++ b/packages/clerk-js/src/core/resources/ApiKey.ts @@ -0,0 +1,61 @@ +import type { ApiKeyJSON, ApiKeyResource } from '@clerk/types'; + +import { unixEpochToDate } from '../../utils/date'; +import { BaseResource } from './internal'; + +export class ApiKey extends BaseResource implements ApiKeyResource { + pathRoot = '/api_keys'; + + id = ''; + type = ''; + name = ''; + subject = ''; + scopes: string[] = []; + claims: Record | null = null; + revoked = false; + revocationReason: string | null = null; + expired = false; + expiration: Date | null = null; + createdBy: string | null = null; + creationReason: string | null = null; + createdAt: Date = new Date(); + updatedAt: Date = new Date(); + + constructor(data: ApiKeyJSON) { + super(); + this.fromJSON(data); + } + + protected fromJSON(data: ApiKeyJSON | null): this { + if (!data) { + return this; + } + + this.id = data.id; + this.expiration = data.expiration ? unixEpochToDate(data.expiration) : null; + this.updatedAt = unixEpochToDate(data.updated_at); + this.createdAt = unixEpochToDate(data.created_at); + return this; + } + + static async getAll(): Promise { + return this.clerk + .getFapiClient() + .request({ + method: 'GET', + path: '/api_keys', + pathPrefix: '', + search: { + subject: this.clerk.user?.id ?? '', + }, + headers: { + Authorization: `Bearer ${await this.clerk.session?.getToken()}`, + }, + }) + .then(res => { + const apiKeysJSON = res.payload as unknown as ApiKeyJSON[]; + return apiKeysJSON.map(json => new ApiKey(json)); + }) + .catch(() => []); + } +} diff --git a/packages/clerk-js/src/core/resources/internal.ts b/packages/clerk-js/src/core/resources/internal.ts index 6ae1cb0c007..bbf1132b415 100644 --- a/packages/clerk-js/src/core/resources/internal.ts +++ b/packages/clerk-js/src/core/resources/internal.ts @@ -42,3 +42,4 @@ export * from './UserOrganizationInvitation'; export * from './Verification'; export * from './Web3Wallet'; export * from './Waitlist'; +export * from './ApiKey'; diff --git a/packages/clerk-js/src/ui/components/ManageApiKeys/ManageApiKeys.tsx b/packages/clerk-js/src/ui/components/ManageApiKeys/ManageApiKeys.tsx index 4cd2679481b..3c0e0f75c01 100644 --- a/packages/clerk-js/src/ui/components/ManageApiKeys/ManageApiKeys.tsx +++ b/packages/clerk-js/src/ui/components/ManageApiKeys/ManageApiKeys.tsx @@ -1,13 +1,17 @@ +import { useClerk } from '@clerk/shared/react'; import { useEffect } from 'react'; -import { useManageApiKeysContext } from '../../contexts'; +// import { useManageApiKeysContext } from '../../contexts'; export const ManageApiKeys = () => { - const ctx = useManageApiKeysContext(); + const clerk = useClerk(); + // const ctx = useManageApiKeysContext(); useEffect(() => { - console.log(ctx); - }, [ctx]); + clerk.getApiKeys().then(apiKeys => { + console.log(apiKeys); + }); + }, [clerk]); return (
    diff --git a/packages/types/src/apiKey.ts b/packages/types/src/apiKey.ts new file mode 100644 index 00000000000..7d5a6e28429 --- /dev/null +++ b/packages/types/src/apiKey.ts @@ -0,0 +1,18 @@ +import type { ClerkResource } from './resource'; + +export interface ApiKeyResource extends ClerkResource { + id: string; + type: string; + name: string; + subject: string; + scopes: string[]; + claims: Record | null; + revoked: boolean; + revocationReason: string | null; + expired: boolean; + expiration: Date | null; + createdBy: string | null; + creationReason: string | null; + createdAt: Date; + updatedAt: Date; +} diff --git a/packages/types/src/clerk.ts b/packages/types/src/clerk.ts index f3e34aad53f..d6b39648f84 100644 --- a/packages/types/src/clerk.ts +++ b/packages/types/src/clerk.ts @@ -1,3 +1,4 @@ +import type { ApiKeyResource } from './apiKey'; import type { Appearance, CheckoutTheme, @@ -753,6 +754,11 @@ export interface Clerk { * initiated outside of the Clerk class. */ __internal_setActiveInProgress: boolean; + + /** + * Retrieves all API keys for the current user. + */ + getApiKeys: () => Promise; } export type HandleOAuthCallbackParams = TransferableOption & @@ -1638,7 +1644,9 @@ type PortalRoot = HTMLElement | null | undefined; export type PricingTableProps = PricingTableBaseProps & PricingTableDefaultProps; export type ManageApiKeysProps = { - [key: string]: unknown; + type?: string; + subject: string; + claims?: string; appearance?: ManageApiKeysTheme; }; diff --git a/packages/types/src/index.ts b/packages/types/src/index.ts index 143e5f7df9c..8586d5b2ba1 100644 --- a/packages/types/src/index.ts +++ b/packages/types/src/index.ts @@ -66,6 +66,7 @@ export * from './passkey'; export * from './customMenuItems'; export * from './samlConnection'; export * from './waitlist'; +export * from './apiKey'; export * from './snapshots'; export * from './authObject'; export * from './phoneCodeChannel'; diff --git a/packages/types/src/json.ts b/packages/types/src/json.ts index ae49c10e6db..a196c27d90b 100644 --- a/packages/types/src/json.ts +++ b/packages/types/src/json.ts @@ -725,3 +725,20 @@ export interface CommerceCheckoutJSON extends ClerkResourceJSON { totals: CommerceCheckoutTotalsJSON; is_immediate_plan_change: boolean; } + +export interface ApiKeyJSON extends ClerkResourceJSON { + id: string; + type: string; + name: string; + subject: string; + scopes: string[]; + claims: Record | null; + revoked: boolean; + revocation_reason: string | null; + expired: boolean; + expiration: number | null; + created_by: string | null; + creation_reason: string | null; + created_at: number; + updated_at: number; +} From a1dfa602250021beb98184b6193816d911d4981c Mon Sep 17 00:00:00 2001 From: Bryce Kalow Date: Tue, 6 May 2025 00:23:37 -0500 Subject: [PATCH 003/116] chore(clerk-js): Remove Clerk.commerce (#5846) --- .changeset/proud-donuts-shop.md | 2 + .changeset/witty-doors-hear.md | 23 +++++++ .../src/ui/contexts/components/Invoices.tsx | 60 +++++++++++++++++++ 3 files changed, 85 insertions(+) create mode 100644 .changeset/proud-donuts-shop.md create mode 100644 .changeset/witty-doors-hear.md create mode 100644 packages/clerk-js/src/ui/contexts/components/Invoices.tsx diff --git a/.changeset/proud-donuts-shop.md b/.changeset/proud-donuts-shop.md new file mode 100644 index 00000000000..a845151cc84 --- /dev/null +++ b/.changeset/proud-donuts-shop.md @@ -0,0 +1,2 @@ +--- +--- diff --git a/.changeset/witty-doors-hear.md b/.changeset/witty-doors-hear.md new file mode 100644 index 00000000000..28178bb81fb --- /dev/null +++ b/.changeset/witty-doors-hear.md @@ -0,0 +1,23 @@ +--- +'@clerk/clerk-js': minor +'@clerk/clerk-react': minor +'@clerk/types': minor +--- + +Expose Clerk Billing APIs. + +## Render the pricing table component +- `Clerk.mountPricingTable` +- `Clerk.unmountPricingTable` + +## Manage payment methods +- `Clerk.[user|organization].initializePaymentSource()` +- `Clerk.[user|organization].addPaymentSource()` +- `Clerk.[user|organization].getPaymentSources()` + +## Billing namespace +- `Clerk.billing` + - `Clerk.billing.getPlans()` + - `Clerk.billing.getSubscriptions()` + - `Clerk.billing.getInvoices()` + - `Clerk.billing.startCheckout()` diff --git a/packages/clerk-js/src/ui/contexts/components/Invoices.tsx b/packages/clerk-js/src/ui/contexts/components/Invoices.tsx new file mode 100644 index 00000000000..bc11ef8bf45 --- /dev/null +++ b/packages/clerk-js/src/ui/contexts/components/Invoices.tsx @@ -0,0 +1,60 @@ +import { useClerk, useOrganization, useUser } from '@clerk/shared/react'; +import type { PropsWithChildren } from 'react'; +import { createContext, useContext } from 'react'; + +import { useFetch } from '../../hooks'; +import type { InvoicesCtx } from '../../types'; +import { useSubscriberTypeContext } from './SubscriberType'; + +const InvoicesContext = createContext(null); + +export const InvoicesContextProvider = ({ children }: PropsWithChildren) => { + const { billing } = useClerk(); + const { organization } = useOrganization(); + const subscriberType = useSubscriberTypeContext(); + const { user } = useUser(); + + const resource = subscriberType === 'org' ? organization : user; + + const { data, isLoading, revalidate } = useFetch( + billing.getInvoices, + { ...(subscriberType === 'org' ? { orgId: organization?.id } : {}) }, + undefined, + `commerce-invoices-${resource?.id}`, + ); + const { data: invoices, total_count: totalCount } = data || { data: [], totalCount: 0 }; + + const getInvoiceById = (invoiceId: string) => { + return invoices.find(invoice => invoice.id === invoiceId); + }; + + return ( + + {children} + + ); +}; + +export const useInvoicesContext = () => { + const context = useContext(InvoicesContext); + + if (!context || context.componentName !== 'Invoices') { + throw new Error('Clerk: useInvoicesContext called outside Invoices.'); + } + + const { componentName, ...ctx } = context; + + return { + ...ctx, + componentName, + }; +}; From cb1961e0c796bc4bbea7b2068094fdaed2ab81e4 Mon Sep 17 00:00:00 2001 From: wobsoriano Date: Tue, 6 May 2025 12:05:01 -0700 Subject: [PATCH 004/116] chore: add simple table with calls to fapi --- packages/clerk-js/src/core/clerk.ts | 4 + packages/clerk-js/src/core/fapiClient.ts | 2 +- .../clerk-js/src/core/resources/ApiKey.ts | 63 ++++++--- .../ManageApiKeys/ManageApiKeys.tsx | 122 +++++++++++++++++- packages/types/src/clerk.ts | 7 + 5 files changed, 176 insertions(+), 22 deletions(-) diff --git a/packages/clerk-js/src/core/clerk.ts b/packages/clerk-js/src/core/clerk.ts index 0f82740d358..f3bfd30613a 100644 --- a/packages/clerk-js/src/core/clerk.ts +++ b/packages/clerk-js/src/core/clerk.ts @@ -1067,6 +1067,10 @@ export class Clerk implements ClerkInterface { return ApiKey.getAll(); }; + public getApiKeySecret = async (id: string): Promise => { + return ApiKey.getSecret(id); + }; + /** * `setActive` can be used to set the active session and/or organization. */ diff --git a/packages/clerk-js/src/core/fapiClient.ts b/packages/clerk-js/src/core/fapiClient.ts index 2ae7bd9a502..609ddff5862 100644 --- a/packages/clerk-js/src/core/fapiClient.ts +++ b/packages/clerk-js/src/core/fapiClient.ts @@ -224,8 +224,8 @@ export function createFapiClient(options: FapiClientOptions): FapiClient { let response: Response; const urlStr = requestInit.url.toString(); const fetchOpts: FapiRequestInit = { - ...requestInit, credentials: 'include', + ...requestInit, method: overwrittenRequestMethod, }; diff --git a/packages/clerk-js/src/core/resources/ApiKey.ts b/packages/clerk-js/src/core/resources/ApiKey.ts index 5dbab295239..fdaaccdfa8f 100644 --- a/packages/clerk-js/src/core/resources/ApiKey.ts +++ b/packages/clerk-js/src/core/resources/ApiKey.ts @@ -6,20 +6,20 @@ import { BaseResource } from './internal'; export class ApiKey extends BaseResource implements ApiKeyResource { pathRoot = '/api_keys'; - id = ''; - type = ''; - name = ''; - subject = ''; - scopes: string[] = []; - claims: Record | null = null; - revoked = false; - revocationReason: string | null = null; - expired = false; - expiration: Date | null = null; - createdBy: string | null = null; - creationReason: string | null = null; - createdAt: Date = new Date(); - updatedAt: Date = new Date(); + id!: string; + type!: string; + name!: string; + subject!: string; + scopes!: string[]; + claims!: Record | null; + revoked!: boolean; + revocationReason!: string | null; + expired!: boolean; + expiration!: Date | null; + createdBy!: string | null; + creationReason!: string | null; + createdAt!: Date; + updatedAt!: Date; constructor(data: ApiKeyJSON) { super(); @@ -32,7 +32,17 @@ export class ApiKey extends BaseResource implements ApiKeyResource { } this.id = data.id; + this.type = data.type; + this.name = data.name; + this.subject = data.subject; + this.scopes = data.scopes; + this.claims = data.claims; + this.revoked = data.revoked; + this.revocationReason = data.revocation_reason; + this.expired = data.expired; this.expiration = data.expiration ? unixEpochToDate(data.expiration) : null; + this.createdBy = data.created_by; + this.creationReason = data.creation_reason; this.updatedAt = unixEpochToDate(data.updated_at); this.createdAt = unixEpochToDate(data.created_at); return this; @@ -41,7 +51,7 @@ export class ApiKey extends BaseResource implements ApiKeyResource { static async getAll(): Promise { return this.clerk .getFapiClient() - .request({ + .request<{ api_keys: ApiKeyJSON[] }>({ method: 'GET', path: '/api_keys', pathPrefix: '', @@ -51,11 +61,30 @@ export class ApiKey extends BaseResource implements ApiKeyResource { headers: { Authorization: `Bearer ${await this.clerk.session?.getToken()}`, }, + credentials: 'same-origin', }) .then(res => { - const apiKeysJSON = res.payload as unknown as ApiKeyJSON[]; - return apiKeysJSON.map(json => new ApiKey(json)); + const apiKeysJSON = res.payload as unknown as { api_keys: ApiKeyJSON[] }; + return apiKeysJSON.api_keys.map(json => new ApiKey(json)); }) .catch(() => []); } + + static async getSecret(id: string): Promise { + return this.clerk + .getFapiClient() + .request<{ secret: string }>({ + method: 'GET', + path: `/api_keys/${id}/secret`, + credentials: 'same-origin', + pathPrefix: '', + headers: { + Authorization: `Bearer ${await this.clerk.session?.getToken()}`, + }, + }) + .then(res => { + return (res.payload as any)?.secret ?? ''; + }) + .catch(() => ''); + } } diff --git a/packages/clerk-js/src/ui/components/ManageApiKeys/ManageApiKeys.tsx b/packages/clerk-js/src/ui/components/ManageApiKeys/ManageApiKeys.tsx index 3c0e0f75c01..b005a93bca1 100644 --- a/packages/clerk-js/src/ui/components/ManageApiKeys/ManageApiKeys.tsx +++ b/packages/clerk-js/src/ui/components/ManageApiKeys/ManageApiKeys.tsx @@ -1,21 +1,135 @@ import { useClerk } from '@clerk/shared/react'; -import { useEffect } from 'react'; +import type { ApiKeyResource } from '@clerk/types'; +import { useEffect, useState } from 'react'; // import { useManageApiKeysContext } from '../../contexts'; export const ManageApiKeys = () => { const clerk = useClerk(); // const ctx = useManageApiKeysContext(); + const [apiKeys, setApiKeys] = useState([]); + const [revealedKeys, setRevealedKeys] = useState>({}); useEffect(() => { clerk.getApiKeys().then(apiKeys => { - console.log(apiKeys); + setApiKeys(apiKeys); }); }, [clerk]); + const toggleSecret = async (id: string) => { + setRevealedKeys(prev => { + if (prev[id]) { + return { ...prev, [id]: null }; + } + return prev; + }); + + if (!revealedKeys[id]) { + const secret = await clerk.getApiKeySecret(id); + setRevealedKeys(prev => ({ ...prev, [id]: secret })); + } + }; + return ( -
    -

    Manage API Keys

    +
    + + + + + + + + + + + {apiKeys.map(apiKey => ( + + + + + + + ))} + +
    NameLast usedKeyActions
    +
    {apiKey.name}
    +
    + Created at{' '} + {apiKey.createdAt.toLocaleDateString(undefined, { month: 'short', day: '2-digit', year: 'numeric' })} +
    +
    + {/* Placeholder for "Last used" */} + 3d ago + + + + + + +
    ); }; diff --git a/packages/types/src/clerk.ts b/packages/types/src/clerk.ts index d6b39648f84..5ea0864f812 100644 --- a/packages/types/src/clerk.ts +++ b/packages/types/src/clerk.ts @@ -759,6 +759,13 @@ export interface Clerk { * Retrieves all API keys for the current user. */ getApiKeys: () => Promise; + + /** + * Retrieves the secret for a given API key ID. + * @param id - The ID of the API key to retrieve the secret for. + * @returns The secret for the given API key ID. + */ + getApiKeySecret: (id: string) => Promise; } export type HandleOAuthCallbackParams = TransferableOption & From 44cfd159468afd1285831b118fe744dc375ed6f4 Mon Sep 17 00:00:00 2001 From: wobsoriano Date: Tue, 6 May 2025 14:33:44 -0700 Subject: [PATCH 005/116] chore: use built in components --- .../ManageApiKeys/ManageApiKeys.tsx | 194 +++++++++--------- 1 file changed, 97 insertions(+), 97 deletions(-) diff --git a/packages/clerk-js/src/ui/components/ManageApiKeys/ManageApiKeys.tsx b/packages/clerk-js/src/ui/components/ManageApiKeys/ManageApiKeys.tsx index b005a93bca1..b6ea38a2701 100644 --- a/packages/clerk-js/src/ui/components/ManageApiKeys/ManageApiKeys.tsx +++ b/packages/clerk-js/src/ui/components/ManageApiKeys/ManageApiKeys.tsx @@ -1,21 +1,15 @@ import { useClerk } from '@clerk/shared/react'; -import type { ApiKeyResource } from '@clerk/types'; -import { useEffect, useState } from 'react'; +import { useState } from 'react'; -// import { useManageApiKeysContext } from '../../contexts'; +import { Button, Flex, Flow, Icon, Input, Table, Tbody, Td, Text, Th, Thead, Tr } from '../../customizables'; +import { useFetch } from '../../hooks'; +import { Clipboard, Eye, EyeSlash, Plus } from '../../icons'; export const ManageApiKeys = () => { const clerk = useClerk(); - // const ctx = useManageApiKeysContext(); - const [apiKeys, setApiKeys] = useState([]); + const { data: apiKeys } = useFetch(clerk.getApiKeys, {}); const [revealedKeys, setRevealedKeys] = useState>({}); - useEffect(() => { - clerk.getApiKeys().then(apiKeys => { - setApiKeys(apiKeys); - }); - }, [clerk]); - const toggleSecret = async (id: string) => { setRevealedKeys(prev => { if (prev[id]) { @@ -31,105 +25,111 @@ export const ManageApiKeys = () => { }; return ( -
    - + - - - - - - - - - - {apiKeys.map(apiKey => ( - - +
    NameLast usedKeyActions
    -
    {apiKey.name}
    -
    + + + + + + + + + + + + + + {apiKeys?.map(apiKey => ( + + - - + + - + - + + + + ))} - -
    NameLast usedKeyActions
    + {apiKey.name} + Created at{' '} - {apiKey.createdAt.toLocaleDateString(undefined, { month: 'short', day: '2-digit', year: 'numeric' })} - - - {/* Placeholder for "Last used" */} - 3d ago - - + + + {/* Placeholder for "Last used" */} + 3d ago + + + - - + - - + + -
    -
    +
    + ); }; From 3a6d206912659f86fe0eeab8db97e44af2a0f381 Mon Sep 17 00:00:00 2001 From: wobsoriano Date: Tue, 6 May 2025 19:05:22 -0700 Subject: [PATCH 006/116] chore: add fake form --- .../ManageApiKeys/ManageApiKeys.tsx | 107 +++++++++++++++++- 1 file changed, 106 insertions(+), 1 deletion(-) diff --git a/packages/clerk-js/src/ui/components/ManageApiKeys/ManageApiKeys.tsx b/packages/clerk-js/src/ui/components/ManageApiKeys/ManageApiKeys.tsx index b6ea38a2701..3cb44b66bd2 100644 --- a/packages/clerk-js/src/ui/components/ManageApiKeys/ManageApiKeys.tsx +++ b/packages/clerk-js/src/ui/components/ManageApiKeys/ManageApiKeys.tsx @@ -1,14 +1,98 @@ import { useClerk } from '@clerk/shared/react'; import { useState } from 'react'; -import { Button, Flex, Flow, Icon, Input, Table, Tbody, Td, Text, Th, Thead, Tr } from '../../customizables'; +import { Box, Button, Flex, Flow, Icon, Input, Table, Tbody, Td, Text, Th, Thead, Tr } from '../../customizables'; import { useFetch } from '../../hooks'; import { Clipboard, Eye, EyeSlash, Plus } from '../../icons'; +// AddApiKeyForm component +const AddApiKeyForm = ({ + onCreate, + onCancel, + loading, +}: { + onCreate: (name: string) => void; + onCancel: () => void; + loading?: boolean; +}) => { + const [name, setName] = useState(''); + + return ( + + Add new API key + Secret key name + setName(e.target.value)} + sx={{ + width: '100%', + fontSize: 16, + mb: 5, + borderRadius: 8, + border: '1px solid #ddd', + background: '#fafafa', + py: 3, + px: 3, + }} + /> + + + + + + ); +}; + export const ManageApiKeys = () => { const clerk = useClerk(); const { data: apiKeys } = useFetch(clerk.getApiKeys, {}); const [revealedKeys, setRevealedKeys] = useState>({}); + const [showAddForm, setShowAddForm] = useState(false); + const [creating, setCreating] = useState(false); const toggleSecret = async (id: string) => { setRevealedKeys(prev => { @@ -24,6 +108,17 @@ export const ManageApiKeys = () => { } }; + const handleCreate = async (_name: string) => { + setCreating(true); + try { + // await clerk.createApiKey({ name }); + setShowAddForm(false); + // refetch?.(); + } finally { + setCreating(false); + } + }; + return ( { /> + + {showAddForm && ( + void handleCreate('')} + onCancel={() => setShowAddForm(false)} + loading={creating} + /> + )} + From e4d26a5b180d294817f6d407903ba13e5d22fbb9 Mon Sep 17 00:00:00 2001 From: wobsoriano Date: Tue, 6 May 2025 19:09:46 -0700 Subject: [PATCH 007/116] chore: fix bad rebase --- .../src/ui/contexts/components/Invoices.tsx | 60 ------------------- 1 file changed, 60 deletions(-) delete mode 100644 packages/clerk-js/src/ui/contexts/components/Invoices.tsx diff --git a/packages/clerk-js/src/ui/contexts/components/Invoices.tsx b/packages/clerk-js/src/ui/contexts/components/Invoices.tsx deleted file mode 100644 index bc11ef8bf45..00000000000 --- a/packages/clerk-js/src/ui/contexts/components/Invoices.tsx +++ /dev/null @@ -1,60 +0,0 @@ -import { useClerk, useOrganization, useUser } from '@clerk/shared/react'; -import type { PropsWithChildren } from 'react'; -import { createContext, useContext } from 'react'; - -import { useFetch } from '../../hooks'; -import type { InvoicesCtx } from '../../types'; -import { useSubscriberTypeContext } from './SubscriberType'; - -const InvoicesContext = createContext(null); - -export const InvoicesContextProvider = ({ children }: PropsWithChildren) => { - const { billing } = useClerk(); - const { organization } = useOrganization(); - const subscriberType = useSubscriberTypeContext(); - const { user } = useUser(); - - const resource = subscriberType === 'org' ? organization : user; - - const { data, isLoading, revalidate } = useFetch( - billing.getInvoices, - { ...(subscriberType === 'org' ? { orgId: organization?.id } : {}) }, - undefined, - `commerce-invoices-${resource?.id}`, - ); - const { data: invoices, total_count: totalCount } = data || { data: [], totalCount: 0 }; - - const getInvoiceById = (invoiceId: string) => { - return invoices.find(invoice => invoice.id === invoiceId); - }; - - return ( - - {children} - - ); -}; - -export const useInvoicesContext = () => { - const context = useContext(InvoicesContext); - - if (!context || context.componentName !== 'Invoices') { - throw new Error('Clerk: useInvoicesContext called outside Invoices.'); - } - - const { componentName, ...ctx } = context; - - return { - ...ctx, - componentName, - }; -}; From 6a914c5f3f68fb2c9fb955b8076258a927d1e0f3 Mon Sep 17 00:00:00 2001 From: wobsoriano Date: Tue, 6 May 2025 19:11:06 -0700 Subject: [PATCH 008/116] chore: fix bad rebase --- .changeset/proud-donuts-shop.md | 2 -- .changeset/witty-doors-hear.md | 23 ----------------------- 2 files changed, 25 deletions(-) delete mode 100644 .changeset/proud-donuts-shop.md delete mode 100644 .changeset/witty-doors-hear.md diff --git a/.changeset/proud-donuts-shop.md b/.changeset/proud-donuts-shop.md deleted file mode 100644 index a845151cc84..00000000000 --- a/.changeset/proud-donuts-shop.md +++ /dev/null @@ -1,2 +0,0 @@ ---- ---- diff --git a/.changeset/witty-doors-hear.md b/.changeset/witty-doors-hear.md deleted file mode 100644 index 28178bb81fb..00000000000 --- a/.changeset/witty-doors-hear.md +++ /dev/null @@ -1,23 +0,0 @@ ---- -'@clerk/clerk-js': minor -'@clerk/clerk-react': minor -'@clerk/types': minor ---- - -Expose Clerk Billing APIs. - -## Render the pricing table component -- `Clerk.mountPricingTable` -- `Clerk.unmountPricingTable` - -## Manage payment methods -- `Clerk.[user|organization].initializePaymentSource()` -- `Clerk.[user|organization].addPaymentSource()` -- `Clerk.[user|organization].getPaymentSources()` - -## Billing namespace -- `Clerk.billing` - - `Clerk.billing.getPlans()` - - `Clerk.billing.getSubscriptions()` - - `Clerk.billing.getInvoices()` - - `Clerk.billing.startCheckout()` From beb0a44c97627d68bf6b534459d24e511d94714e Mon Sep 17 00:00:00 2001 From: wobsoriano Date: Tue, 6 May 2025 19:34:56 -0700 Subject: [PATCH 009/116] chore: add create api key func --- packages/clerk-js/src/core/clerk.ts | 4 + .../clerk-js/src/core/resources/ApiKey.ts | 31 ++++++ .../ManageApiKeys/CreateApiKeyForm.tsx | 54 +++++++++ .../ManageApiKeys/ManageApiKeys.tsx | 104 ++---------------- packages/types/src/clerk.ts | 7 ++ 5 files changed, 104 insertions(+), 96 deletions(-) create mode 100644 packages/clerk-js/src/ui/components/ManageApiKeys/CreateApiKeyForm.tsx diff --git a/packages/clerk-js/src/core/clerk.ts b/packages/clerk-js/src/core/clerk.ts index f3bfd30613a..315ee85815e 100644 --- a/packages/clerk-js/src/core/clerk.ts +++ b/packages/clerk-js/src/core/clerk.ts @@ -1071,6 +1071,10 @@ export class Clerk implements ClerkInterface { return ApiKey.getSecret(id); }; + public createApiKey = async (name: string): Promise => { + return ApiKey.create(name); + }; + /** * `setActive` can be used to set the active session and/or organization. */ diff --git a/packages/clerk-js/src/core/resources/ApiKey.ts b/packages/clerk-js/src/core/resources/ApiKey.ts index fdaaccdfa8f..b38f7aeed31 100644 --- a/packages/clerk-js/src/core/resources/ApiKey.ts +++ b/packages/clerk-js/src/core/resources/ApiKey.ts @@ -87,4 +87,35 @@ export class ApiKey extends BaseResource implements ApiKeyResource { }) .catch(() => ''); } + + static async create(name: string): Promise { + return this.clerk + .getFapiClient() + .request({ + method: 'POST', + path: '/api_keys', + pathPrefix: '', + search: { + subject: this.clerk.user?.id ?? '', + }, + headers: { + Authorization: `Bearer ${await this.clerk.session?.getToken()}`, + 'Content-Type': 'application/json', + }, + credentials: 'same-origin', + body: JSON.stringify({ + type: 'api_key', + name, + subject: this.clerk.user?.id ?? '', + claims: null, + scopes: [], + creation_reason: null, + seconds_until_expiration: null, + }), + }) + .then(res => { + const apiKeysJSON = res.payload as unknown as ApiKeyJSON; + return new ApiKey(apiKeysJSON); + }); + } } diff --git a/packages/clerk-js/src/ui/components/ManageApiKeys/CreateApiKeyForm.tsx b/packages/clerk-js/src/ui/components/ManageApiKeys/CreateApiKeyForm.tsx new file mode 100644 index 00000000000..f2a318150d0 --- /dev/null +++ b/packages/clerk-js/src/ui/components/ManageApiKeys/CreateApiKeyForm.tsx @@ -0,0 +1,54 @@ +import { useState } from 'react'; + +import { Box, Button, Flex, Input, Text } from '../../customizables'; + +export const CreateApiKeyForm = ({ + onCreate, + onCancel, + loading, +}: { + onCreate: (name: string) => void; + onCancel: () => void; + loading?: boolean; +}) => { + const [name, setName] = useState(''); + + return ( + + Add new API key + Secret key name + setName(e.target.value)} + sx={{ + width: '100%', + }} + /> + + + + + + ); +}; diff --git a/packages/clerk-js/src/ui/components/ManageApiKeys/ManageApiKeys.tsx b/packages/clerk-js/src/ui/components/ManageApiKeys/ManageApiKeys.tsx index 3cb44b66bd2..4e541e1910e 100644 --- a/packages/clerk-js/src/ui/components/ManageApiKeys/ManageApiKeys.tsx +++ b/packages/clerk-js/src/ui/components/ManageApiKeys/ManageApiKeys.tsx @@ -1,95 +1,14 @@ import { useClerk } from '@clerk/shared/react'; import { useState } from 'react'; -import { Box, Button, Flex, Flow, Icon, Input, Table, Tbody, Td, Text, Th, Thead, Tr } from '../../customizables'; +import { Button, Flex, Flow, Icon, Input, Table, Tbody, Td, Text, Th, Thead, Tr } from '../../customizables'; import { useFetch } from '../../hooks'; import { Clipboard, Eye, EyeSlash, Plus } from '../../icons'; - -// AddApiKeyForm component -const AddApiKeyForm = ({ - onCreate, - onCancel, - loading, -}: { - onCreate: (name: string) => void; - onCancel: () => void; - loading?: boolean; -}) => { - const [name, setName] = useState(''); - - return ( - - Add new API key - Secret key name - setName(e.target.value)} - sx={{ - width: '100%', - fontSize: 16, - mb: 5, - borderRadius: 8, - border: '1px solid #ddd', - background: '#fafafa', - py: 3, - px: 3, - }} - /> - - - - - - ); -}; +import { CreateApiKeyForm } from './CreateApiKeyForm'; export const ManageApiKeys = () => { const clerk = useClerk(); - const { data: apiKeys } = useFetch(clerk.getApiKeys, {}); + const { data: apiKeys, revalidate } = useFetch(clerk.getApiKeys, {}); const [revealedKeys, setRevealedKeys] = useState>({}); const [showAddForm, setShowAddForm] = useState(false); const [creating, setCreating] = useState(false); @@ -108,12 +27,12 @@ export const ManageApiKeys = () => { } }; - const handleCreate = async (_name: string) => { + const handleCreate = async (name: string) => { setCreating(true); try { - // await clerk.createApiKey({ name }); + await clerk.createApiKey(name); setShowAddForm(false); - // refetch?.(); + revalidate(); } finally { setCreating(false); } @@ -138,21 +57,14 @@ export const ManageApiKeys = () => { {showAddForm && ( - void handleCreate('')} + void handleCreate(name)} onCancel={() => setShowAddForm(false)} loading={creating} /> diff --git a/packages/types/src/clerk.ts b/packages/types/src/clerk.ts index 5ea0864f812..aab589d8f99 100644 --- a/packages/types/src/clerk.ts +++ b/packages/types/src/clerk.ts @@ -766,6 +766,13 @@ export interface Clerk { * @returns The secret for the given API key ID. */ getApiKeySecret: (id: string) => Promise; + + /** + * Creates a new API key. + * @param name - The name of the API key. + * @returns The created API key. + */ + createApiKey: (name: string) => Promise; } export type HandleOAuthCallbackParams = TransferableOption & From 7294f7e82a46acff6e77190eed21e22f4cbf35fa Mon Sep 17 00:00:00 2001 From: wobsoriano Date: Wed, 7 May 2025 11:20:57 -0700 Subject: [PATCH 010/116] chore: add prop types and improve fetching --- packages/clerk-js/src/core/clerk.ts | 9 +++-- .../clerk-js/src/core/resources/ApiKey.ts | 38 ++++++++----------- .../ManageApiKeys/ManageApiKeys.tsx | 16 ++++---- packages/types/src/clerk.ts | 26 +++++++------ 4 files changed, 44 insertions(+), 45 deletions(-) diff --git a/packages/clerk-js/src/core/clerk.ts b/packages/clerk-js/src/core/clerk.ts index 315ee85815e..9b47d61b9a8 100644 --- a/packages/clerk-js/src/core/clerk.ts +++ b/packages/clerk-js/src/core/clerk.ts @@ -31,6 +31,7 @@ import type { ClientJSONSnapshot, ClientResource, CommerceBillingNamespace, + CreateApiKeyParams, CreateOrganizationParams, CreateOrganizationProps, CredentialReturn, @@ -1067,12 +1068,12 @@ export class Clerk implements ClerkInterface { return ApiKey.getAll(); }; - public getApiKeySecret = async (id: string): Promise => { - return ApiKey.getSecret(id); + public getApiKeySecret = async (apiKeyID: string): Promise => { + return ApiKey.getSecret(apiKeyID); }; - public createApiKey = async (name: string): Promise => { - return ApiKey.create(name); + public createApiKey = async (params: CreateApiKeyParams): Promise => { + return ApiKey.create(params); }; /** diff --git a/packages/clerk-js/src/core/resources/ApiKey.ts b/packages/clerk-js/src/core/resources/ApiKey.ts index b38f7aeed31..477104b1238 100644 --- a/packages/clerk-js/src/core/resources/ApiKey.ts +++ b/packages/clerk-js/src/core/resources/ApiKey.ts @@ -1,4 +1,4 @@ -import type { ApiKeyJSON, ApiKeyResource } from '@clerk/types'; +import type { ApiKeyJSON, ApiKeyResource, CreateApiKeyParams } from '@clerk/types'; import { unixEpochToDate } from '../../utils/date'; import { BaseResource } from './internal'; @@ -48,7 +48,7 @@ export class ApiKey extends BaseResource implements ApiKeyResource { return this; } - static async getAll(): Promise { + static async getAll(params?: { subject?: string }): Promise { return this.clerk .getFapiClient() .request<{ api_keys: ApiKeyJSON[] }>({ @@ -56,7 +56,7 @@ export class ApiKey extends BaseResource implements ApiKeyResource { path: '/api_keys', pathPrefix: '', search: { - subject: this.clerk.user?.id ?? '', + subject: params?.subject ?? this.clerk.organization?.id ?? this.clerk.user?.id ?? '', }, headers: { Authorization: `Bearer ${await this.clerk.session?.getToken()}`, @@ -83,39 +83,31 @@ export class ApiKey extends BaseResource implements ApiKeyResource { }, }) .then(res => { - return (res.payload as any)?.secret ?? ''; + const { secret } = res.payload as unknown as { secret: string }; + return secret; }) .catch(() => ''); } - static async create(name: string): Promise { - return this.clerk - .getFapiClient() - .request({ - method: 'POST', + static async create(params: CreateApiKeyParams): Promise { + const json = ( + await BaseResource._fetch({ path: '/api_keys', + method: 'POST', pathPrefix: '', - search: { - subject: this.clerk.user?.id ?? '', - }, headers: { Authorization: `Bearer ${await this.clerk.session?.getToken()}`, 'Content-Type': 'application/json', }, credentials: 'same-origin', body: JSON.stringify({ - type: 'api_key', - name, - subject: this.clerk.user?.id ?? '', - claims: null, - scopes: [], - creation_reason: null, - seconds_until_expiration: null, + ...params, + type: params.type ?? 'api_key', + subject: params.subject ?? this.clerk.organization?.id ?? this.clerk.user?.id ?? '', }), }) - .then(res => { - const apiKeysJSON = res.payload as unknown as ApiKeyJSON; - return new ApiKey(apiKeysJSON); - }); + )?.response as ApiKeyJSON; + + return new ApiKey(json); } } diff --git a/packages/clerk-js/src/ui/components/ManageApiKeys/ManageApiKeys.tsx b/packages/clerk-js/src/ui/components/ManageApiKeys/ManageApiKeys.tsx index 4e541e1910e..fb8b7e7cc34 100644 --- a/packages/clerk-js/src/ui/components/ManageApiKeys/ManageApiKeys.tsx +++ b/packages/clerk-js/src/ui/components/ManageApiKeys/ManageApiKeys.tsx @@ -1,6 +1,7 @@ import { useClerk } from '@clerk/shared/react'; import { useState } from 'react'; +import { useManageApiKeysContext } from '../../contexts'; import { Button, Flex, Flow, Icon, Input, Table, Tbody, Td, Text, Th, Thead, Tr } from '../../customizables'; import { useFetch } from '../../hooks'; import { Clipboard, Eye, EyeSlash, Plus } from '../../icons'; @@ -8,9 +9,10 @@ import { CreateApiKeyForm } from './CreateApiKeyForm'; export const ManageApiKeys = () => { const clerk = useClerk(); - const { data: apiKeys, revalidate } = useFetch(clerk.getApiKeys, {}); + const ctx = useManageApiKeysContext(); + const { data: apiKeys, revalidate } = useFetch(() => clerk.getApiKeys({ subject: ctx.subject }), {}); const [revealedKeys, setRevealedKeys] = useState>({}); - const [showAddForm, setShowAddForm] = useState(false); + const [showCreateForm, setShowCreateForm] = useState(false); const [creating, setCreating] = useState(false); const toggleSecret = async (id: string) => { @@ -30,8 +32,8 @@ export const ManageApiKeys = () => { const handleCreate = async (name: string) => { setCreating(true); try { - await clerk.createApiKey(name); - setShowAddForm(false); + await clerk.createApiKey({ name }); + setShowCreateForm(false); revalidate(); } finally { setCreating(false); @@ -56,16 +58,16 @@ export const ManageApiKeys = () => { /> - {showAddForm && ( + {showCreateForm && ( void handleCreate(name)} - onCancel={() => setShowAddForm(false)} + onCancel={() => setShowCreateForm(false)} loading={creating} /> )} diff --git a/packages/types/src/clerk.ts b/packages/types/src/clerk.ts index aab589d8f99..a456dc60be4 100644 --- a/packages/types/src/clerk.ts +++ b/packages/types/src/clerk.ts @@ -756,23 +756,19 @@ export interface Clerk { __internal_setActiveInProgress: boolean; /** - * Retrieves all API keys for the current user. + * Retrieves all API keys for the current user or organization. */ - getApiKeys: () => Promise; + getApiKeys: (params?: { subject?: string }) => Promise; /** * Retrieves the secret for a given API key ID. - * @param id - The ID of the API key to retrieve the secret for. - * @returns The secret for the given API key ID. */ - getApiKeySecret: (id: string) => Promise; + getApiKeySecret: (apiKeyID: string) => Promise; /** * Creates a new API key. - * @param name - The name of the API key. - * @returns The created API key. */ - createApiKey: (name: string) => Promise; + createApiKey: (params: CreateApiKeyParams) => Promise; } export type HandleOAuthCallbackParams = TransferableOption & @@ -1658,12 +1654,20 @@ type PortalRoot = HTMLElement | null | undefined; export type PricingTableProps = PricingTableBaseProps & PricingTableDefaultProps; export type ManageApiKeysProps = { - type?: string; - subject: string; - claims?: string; + type?: 'api_key'; + subject?: string; appearance?: ManageApiKeysTheme; }; +export type CreateApiKeyParams = { + type?: 'api_key'; + name: string; + subject?: string; + claims?: string | Record; + scopes?: string[]; + creationReason?: string; +}; + export type __internal_CheckoutProps = { appearance?: CheckoutTheme; planId?: string; From b2ff5a4ffae04cd979ee015d53fa427440d46a4b Mon Sep 17 00:00:00 2001 From: wobsoriano Date: Wed, 7 May 2025 12:01:25 -0700 Subject: [PATCH 011/116] chore: add React component --- packages/react/src/components/index.ts | 1 + .../react/src/components/uiComponents.tsx | 29 +++++++++++ packages/react/src/isomorphicClerk.ts | 51 +++++++++++++++++++ 3 files changed, 81 insertions(+) diff --git a/packages/react/src/components/index.ts b/packages/react/src/components/index.ts index f932a18115d..4b988ca6f0a 100644 --- a/packages/react/src/components/index.ts +++ b/packages/react/src/components/index.ts @@ -10,6 +10,7 @@ export { GoogleOneTap, Waitlist, PricingTable, + ManageApiKeys, } from './uiComponents'; export { diff --git a/packages/react/src/components/uiComponents.tsx b/packages/react/src/components/uiComponents.tsx index efba0fe8130..4c65ecba6b8 100644 --- a/packages/react/src/components/uiComponents.tsx +++ b/packages/react/src/components/uiComponents.tsx @@ -2,6 +2,7 @@ import { logErrorInDevMode } from '@clerk/shared/utils'; import type { CreateOrganizationProps, GoogleOneTapProps, + ManageApiKeysProps, OrganizationListProps, OrganizationProfileProps, OrganizationSwitcherProps, @@ -600,3 +601,31 @@ export const PricingTable = withClerk( }, { component: 'PricingTable', renderWhileLoading: true }, ); + +export const ManageApiKeys = withClerk( + ({ clerk, component, fallback, ...props }: WithClerkProp) => { + const mountingStatus = useWaitForComponentMount(component); + const shouldShowFallback = mountingStatus === 'rendering' || !clerk.loaded; + + const rendererRootProps = { + ...(shouldShowFallback && fallback && { style: { display: 'none' } }), + }; + + return ( + <> + {shouldShowFallback && fallback} + {clerk.loaded && ( + + )} + + ); + }, + { component: 'ManageApiKeys', renderWhileLoading: true }, +); diff --git a/packages/react/src/isomorphicClerk.ts b/packages/react/src/isomorphicClerk.ts index d9b18bf73c3..cc89ba8baaa 100644 --- a/packages/react/src/isomorphicClerk.ts +++ b/packages/react/src/isomorphicClerk.ts @@ -7,6 +7,7 @@ import type { __internal_PlanDetailsProps, __internal_UserVerificationModalProps, __internal_UserVerificationProps, + ApiKeyResource, AuthenticateWithCoinbaseWalletParams, AuthenticateWithGoogleOneTapParams, AuthenticateWithMetamaskParams, @@ -17,6 +18,7 @@ import type { ClerkStatus, ClientResource, CommerceBillingNamespace, + CreateApiKeyParams, CreateOrganizationParams, CreateOrganizationProps, DomainOrProxyUrl, @@ -26,6 +28,7 @@ import type { JoinWaitlistParams, ListenerCallback, LoadedClerk, + ManageApiKeysProps, NextTaskParams, OrganizationListProps, OrganizationProfileProps, @@ -131,6 +134,7 @@ export class IsomorphicClerk implements IsomorphicLoadedClerk { private premountMethodCalls = new Map, MethodCallback>(); private premountWaitlistNodes = new Map(); private premountPricingTableNodes = new Map(); + private premountManageApiKeysNodes = new Map(); // A separate Map of `addListener` method calls to handle multiple listeners. private premountAddListenerCalls = new Map< ListenerCallback, @@ -609,6 +613,10 @@ export class IsomorphicClerk implements IsomorphicLoadedClerk { clerkjs.mountPricingTable(node, props); }); + this.premountManageApiKeysNodes.forEach((props, node) => { + clerkjs.mountManageApiKeys(node, props); + }); + /** * Only update status in case `clerk.status` is missing. In any other case, `clerk-js` should be the orchestrator. */ @@ -1050,6 +1058,22 @@ export class IsomorphicClerk implements IsomorphicLoadedClerk { } }; + mountManageApiKeys = (node: HTMLDivElement, props?: ManageApiKeysProps): void => { + if (this.clerkjs && this.loaded) { + this.clerkjs.mountManageApiKeys(node, props); + } else { + this.premountManageApiKeysNodes.set(node, props); + } + }; + + unmountManageApiKeys = (node: HTMLDivElement): void => { + if (this.clerkjs && this.loaded) { + this.clerkjs.unmountManageApiKeys(node); + } else { + this.premountManageApiKeysNodes.delete(node); + } + }; + addListener = (listener: ListenerCallback): UnsubscribeCallback => { if (this.clerkjs) { return this.clerkjs.addListener(listener); @@ -1286,6 +1310,33 @@ export class IsomorphicClerk implements IsomorphicLoadedClerk { } }; + getApiKeys = async (): Promise => { + const callback = () => this.clerkjs?.getApiKeys(); + if (this.clerkjs && this.loaded) { + return callback() as Promise; + } else { + this.premountMethodCalls.set('getApiKeys', callback); + } + }; + + getApiKeySecret = async (apiKeyId: string): Promise => { + const callback = () => this.clerkjs?.getApiKeySecret(apiKeyId); + if (this.clerkjs && this.loaded) { + return callback() as Promise; + } else { + this.premountMethodCalls.set('getApiKeySecret', callback); + } + }; + + createApiKey = async (params: CreateApiKeyParams): Promise => { + const callback = () => this.clerkjs?.createApiKey(params); + if (this.clerkjs && this.loaded) { + return callback() as Promise; + } else { + this.premountMethodCalls.set('createApiKey', callback); + } + }; + signOut = async (...args: Parameters) => { const callback = () => this.clerkjs?.signOut(...args); if (this.clerkjs && this.loaded) { From 83789d0082e1dbe10c8c57d3faca5635e85d8f77 Mon Sep 17 00:00:00 2001 From: wobsoriano Date: Wed, 7 May 2025 13:28:28 -0700 Subject: [PATCH 012/116] chore: accept props --- packages/clerk-js/src/core/clerk.ts | 5 +++-- packages/clerk-js/src/core/resources/ApiKey.ts | 4 ++-- packages/react/src/isomorphicClerk.ts | 5 +++-- packages/types/src/clerk.ts | 4 ++++ 4 files changed, 12 insertions(+), 6 deletions(-) diff --git a/packages/clerk-js/src/core/clerk.ts b/packages/clerk-js/src/core/clerk.ts index 9b47d61b9a8..4b9a0774ef3 100644 --- a/packages/clerk-js/src/core/clerk.ts +++ b/packages/clerk-js/src/core/clerk.ts @@ -39,6 +39,7 @@ import type { EnvironmentJSON, EnvironmentJSONSnapshot, EnvironmentResource, + GetApiKeysParams, GoogleOneTapProps, HandleEmailLinkVerificationParams, HandleOAuthCallbackParams, @@ -1064,8 +1065,8 @@ export class Clerk implements ClerkInterface { ); }; - public getApiKeys = async (): Promise => { - return ApiKey.getAll(); + public getApiKeys = async (params?: GetApiKeysParams): Promise => { + return ApiKey.getAll(params); }; public getApiKeySecret = async (apiKeyID: string): Promise => { diff --git a/packages/clerk-js/src/core/resources/ApiKey.ts b/packages/clerk-js/src/core/resources/ApiKey.ts index 477104b1238..0940f325d46 100644 --- a/packages/clerk-js/src/core/resources/ApiKey.ts +++ b/packages/clerk-js/src/core/resources/ApiKey.ts @@ -1,4 +1,4 @@ -import type { ApiKeyJSON, ApiKeyResource, CreateApiKeyParams } from '@clerk/types'; +import type { ApiKeyJSON, ApiKeyResource, CreateApiKeyParams, GetApiKeysParams } from '@clerk/types'; import { unixEpochToDate } from '../../utils/date'; import { BaseResource } from './internal'; @@ -48,7 +48,7 @@ export class ApiKey extends BaseResource implements ApiKeyResource { return this; } - static async getAll(params?: { subject?: string }): Promise { + static async getAll(params?: GetApiKeysParams): Promise { return this.clerk .getFapiClient() .request<{ api_keys: ApiKeyJSON[] }>({ diff --git a/packages/react/src/isomorphicClerk.ts b/packages/react/src/isomorphicClerk.ts index cc89ba8baaa..0a217f95661 100644 --- a/packages/react/src/isomorphicClerk.ts +++ b/packages/react/src/isomorphicClerk.ts @@ -22,6 +22,7 @@ import type { CreateOrganizationParams, CreateOrganizationProps, DomainOrProxyUrl, + GetApiKeysParams, GoogleOneTapProps, HandleEmailLinkVerificationParams, HandleOAuthCallbackParams, @@ -1310,8 +1311,8 @@ export class IsomorphicClerk implements IsomorphicLoadedClerk { } }; - getApiKeys = async (): Promise => { - const callback = () => this.clerkjs?.getApiKeys(); + getApiKeys = async (params?: GetApiKeysParams): Promise => { + const callback = () => this.clerkjs?.getApiKeys(params); if (this.clerkjs && this.loaded) { return callback() as Promise; } else { diff --git a/packages/types/src/clerk.ts b/packages/types/src/clerk.ts index a456dc60be4..f18c0ee2edb 100644 --- a/packages/types/src/clerk.ts +++ b/packages/types/src/clerk.ts @@ -1659,6 +1659,10 @@ export type ManageApiKeysProps = { appearance?: ManageApiKeysTheme; }; +export type GetApiKeysParams = { + subject?: string; +}; + export type CreateApiKeyParams = { type?: 'api_key'; name: string; From de59b534339e0ed54332bd51113067109ceecd33 Mon Sep 17 00:00:00 2001 From: wobsoriano Date: Wed, 7 May 2025 13:35:30 -0700 Subject: [PATCH 013/116] chore: add copy button functionality --- .../ManageApiKeys/ManageApiKeys.tsx | 29 ++++++++++++------- 1 file changed, 19 insertions(+), 10 deletions(-) diff --git a/packages/clerk-js/src/ui/components/ManageApiKeys/ManageApiKeys.tsx b/packages/clerk-js/src/ui/components/ManageApiKeys/ManageApiKeys.tsx index fb8b7e7cc34..c6dc391e087 100644 --- a/packages/clerk-js/src/ui/components/ManageApiKeys/ManageApiKeys.tsx +++ b/packages/clerk-js/src/ui/components/ManageApiKeys/ManageApiKeys.tsx @@ -3,10 +3,26 @@ import { useState } from 'react'; import { useManageApiKeysContext } from '../../contexts'; import { Button, Flex, Flow, Icon, Input, Table, Tbody, Td, Text, Th, Thead, Tr } from '../../customizables'; -import { useFetch } from '../../hooks'; +import { useClipboard, useFetch } from '../../hooks'; import { Clipboard, Eye, EyeSlash, Plus } from '../../icons'; import { CreateApiKeyForm } from './CreateApiKeyForm'; +const CopyButton = ({ text }: { text: string }) => { + const { onCopy, hasCopied } = useClipboard(text); + + return ( + + ); +}; + export const ManageApiKeys = () => { const clerk = useClerk(); const ctx = useManageApiKeysContext(); @@ -60,7 +76,7 @@ export const ManageApiKeys = () => { variant='solid' onClick={() => setShowCreateForm(true)} > - + Add new key + Add new key @@ -127,14 +143,7 @@ export const ManageApiKeys = () => { > - + diff --git a/packages/react/src/isomorphicClerk.ts b/packages/react/src/isomorphicClerk.ts index 0a217f95661..7b8c3255b5e 100644 --- a/packages/react/src/isomorphicClerk.ts +++ b/packages/react/src/isomorphicClerk.ts @@ -37,6 +37,7 @@ import type { OrganizationSwitcherProps, PricingTableProps, RedirectOptions, + RevokeApiKeyParams, SetActiveParams, SignInProps, SignInRedirectOptions, @@ -1338,6 +1339,15 @@ export class IsomorphicClerk implements IsomorphicLoadedClerk { } }; + revokeApiKey = async (params: RevokeApiKeyParams): Promise => { + const callback = () => this.clerkjs?.revokeApiKey(params); + if (this.clerkjs && this.loaded) { + return callback() as Promise; + } else { + this.premountMethodCalls.set('revokeApiKey', callback); + } + }; + signOut = async (...args: Parameters) => { const callback = () => this.clerkjs?.signOut(...args); if (this.clerkjs && this.loaded) { diff --git a/packages/types/src/clerk.ts b/packages/types/src/clerk.ts index f18c0ee2edb..f65eccc5301 100644 --- a/packages/types/src/clerk.ts +++ b/packages/types/src/clerk.ts @@ -769,6 +769,11 @@ export interface Clerk { * Creates a new API key. */ createApiKey: (params: CreateApiKeyParams) => Promise; + + /** + * Revokes a given API key by ID. + */ + revokeApiKey: (params: RevokeApiKeyParams) => Promise; } export type HandleOAuthCallbackParams = TransferableOption & @@ -1672,6 +1677,11 @@ export type CreateApiKeyParams = { creationReason?: string; }; +export type RevokeApiKeyParams = { + apiKeyID: string; + revocationReason?: string; +}; + export type __internal_CheckoutProps = { appearance?: CheckoutTheme; planId?: string; From a0feb7a7eff99e2a612d78b7875cd2741c5cb879 Mon Sep 17 00:00:00 2001 From: wobsoriano Date: Thu, 8 May 2025 10:14:52 -0700 Subject: [PATCH 016/116] chore: set minimum fields --- packages/clerk-js/src/core/clerk.ts | 24 ++-- .../clerk-js/src/core/resources/ApiKey.ts | 3 +- .../ManageApiKeys/CreateApiKeyForm.tsx | 136 +++++++++++++----- .../ManageApiKeys/ManageApiKeys.tsx | 7 +- packages/types/src/clerk.ts | 7 +- 5 files changed, 119 insertions(+), 58 deletions(-) diff --git a/packages/clerk-js/src/core/clerk.ts b/packages/clerk-js/src/core/clerk.ts index 495ee9f3f66..6adf308614b 100644 --- a/packages/clerk-js/src/core/clerk.ts +++ b/packages/clerk-js/src/core/clerk.ts @@ -1066,22 +1066,6 @@ export class Clerk implements ClerkInterface { ); }; - public getApiKeys = async (params?: GetApiKeysParams): Promise => { - return ApiKey.getAll(params); - }; - - public getApiKeySecret = async (apiKeyID: string): Promise => { - return ApiKey.getSecret(apiKeyID); - }; - - public createApiKey = async (params: CreateApiKeyParams): Promise => { - return ApiKey.create(params); - }; - - public revokeApiKey = async (params: RevokeApiKeyParams): Promise => { - return ApiKey.revoke(params); - }; - /** * `setActive` can be used to set the active session and/or organization. */ @@ -2068,6 +2052,14 @@ export class Clerk implements ClerkInterface { this.environment = environment; } + public getApiKeys = (params?: GetApiKeysParams): Promise => ApiKey.getAll(params); + + public getApiKeySecret = (apiKeyID: string): Promise => ApiKey.getSecret(apiKeyID); + + public createApiKey = (params: CreateApiKeyParams): Promise => ApiKey.create(params); + + public revokeApiKey = (params: RevokeApiKeyParams): Promise => ApiKey.revoke(params); + __internal_setCountry = (country: string | null) => { if (!this.__internal_country) { this.__internal_country = country; diff --git a/packages/clerk-js/src/core/resources/ApiKey.ts b/packages/clerk-js/src/core/resources/ApiKey.ts index e23c218cab5..46646d862b7 100644 --- a/packages/clerk-js/src/core/resources/ApiKey.ts +++ b/packages/clerk-js/src/core/resources/ApiKey.ts @@ -107,9 +107,10 @@ export class ApiKey extends BaseResource implements ApiKeyResource { }, credentials: 'same-origin', body: JSON.stringify({ - ...params, type: params.type ?? 'api_key', subject: params.subject ?? this.clerk.organization?.id ?? this.clerk.user?.id ?? '', + creation_reason: params.creationReason, + seconds_until_expiration: params.secondsUntilExpiration, }), }) )?.response as ApiKeyJSON; diff --git a/packages/clerk-js/src/ui/components/ManageApiKeys/CreateApiKeyForm.tsx b/packages/clerk-js/src/ui/components/ManageApiKeys/CreateApiKeyForm.tsx index f2a318150d0..6a40c20f861 100644 --- a/packages/clerk-js/src/ui/components/ManageApiKeys/CreateApiKeyForm.tsx +++ b/packages/clerk-js/src/ui/components/ManageApiKeys/CreateApiKeyForm.tsx @@ -1,53 +1,119 @@ -import { useState } from 'react'; +import React, { useState } from 'react'; import { Box, Button, Flex, Input, Text } from '../../customizables'; +import { Select } from '../../elements'; -export const CreateApiKeyForm = ({ - onCreate, - onCancel, - loading, -}: { - onCreate: (name: string) => void; +interface CreateApiKeyFormProps { + onCreate: (params: { name: string; description?: string; expiration?: string }) => void; onCancel: () => void; loading?: boolean; -}) => { +} + +const DURATIONS = [ + { label: 'Never', value: '' }, + { label: '30 days', value: '30d' }, + { label: '90 days', value: '90d' }, + // { label: 'Custom', value: 'custom' }, +]; + +export const CreateApiKeyForm: React.FC = ({ onCreate, onCancel, loading }) => { const [name, setName] = useState(''); + const [showAdvanced, setShowAdvanced] = useState(false); + const [expiration, setExpiration] = useState(''); + const [description, setDescription] = useState(''); + + const handleSubmit = (e: React.FormEvent) => { + e.preventDefault(); + onCreate({ + name, + description: showAdvanced ? description : undefined, + expiration: showAdvanced ? expiration : undefined, + }); + }; return ( - - Add new API key - Secret key name - setName(e.target.value)} - sx={{ - width: '100%', - }} - /> + + - + {showAdvanced && ( + <> + + + Description + + setDescription(e.target.value)} + required + /> + + + + Expiration + + setName(e.target.value)} - required - /> - - + + + + {showAdvanced && ( - <> - - - Description - - setDescription(e.target.value)} - required - /> - - - - Expiration - - + + } + /> + - {apiKeys?.map(apiKey => ( + {paginatedApiKeys.map(apiKey => (
    - +
    {apiKey.name} @@ -173,6 +175,18 @@ export const ManageApiKeys = () => { ))}
    -
    + + + ); }; diff --git a/packages/localizations/src/ar-SA.ts b/packages/localizations/src/ar-SA.ts index c0f159d36aa..6934d5fa82e 100644 --- a/packages/localizations/src/ar-SA.ts +++ b/packages/localizations/src/ar-SA.ts @@ -14,6 +14,12 @@ import type { LocalizationResource } from '@clerk/types'; export const arSA: LocalizationResource = { locale: 'ar-SA', + apiKey: { + apiKeyPage: { + formHint: undefined, + title: undefined, + }, + }, backButton: 'الرجوع', badge__activePlan: undefined, badge__canceledEndsAt: undefined, diff --git a/packages/localizations/src/be-BY.ts b/packages/localizations/src/be-BY.ts index a032a076f5e..f0302ef4318 100644 --- a/packages/localizations/src/be-BY.ts +++ b/packages/localizations/src/be-BY.ts @@ -14,6 +14,12 @@ import type { LocalizationResource } from '@clerk/types'; export const beBY: LocalizationResource = { locale: 'be-BY', + apiKey: { + apiKeyPage: { + formHint: undefined, + title: undefined, + }, + }, backButton: 'Назад', badge__activePlan: undefined, badge__canceledEndsAt: undefined, diff --git a/packages/localizations/src/bg-BG.ts b/packages/localizations/src/bg-BG.ts index 3f411bd7f56..9807a416191 100644 --- a/packages/localizations/src/bg-BG.ts +++ b/packages/localizations/src/bg-BG.ts @@ -14,6 +14,12 @@ import type { LocalizationResource } from '@clerk/types'; export const bgBG: LocalizationResource = { locale: 'bg-BG', + apiKey: { + apiKeyPage: { + formHint: undefined, + title: undefined, + }, + }, backButton: 'Назад', badge__activePlan: undefined, badge__canceledEndsAt: undefined, diff --git a/packages/localizations/src/ca-ES.ts b/packages/localizations/src/ca-ES.ts index bbed1e8b6cf..17f9fa76608 100644 --- a/packages/localizations/src/ca-ES.ts +++ b/packages/localizations/src/ca-ES.ts @@ -14,6 +14,12 @@ import type { LocalizationResource } from '@clerk/types'; export const caES: LocalizationResource = { locale: 'ca-ES', + apiKey: { + apiKeyPage: { + formHint: undefined, + title: undefined, + }, + }, backButton: 'Enrere', badge__activePlan: undefined, badge__canceledEndsAt: undefined, diff --git a/packages/localizations/src/cs-CZ.ts b/packages/localizations/src/cs-CZ.ts index 42fdb6f29e5..2dcc0aa3f49 100644 --- a/packages/localizations/src/cs-CZ.ts +++ b/packages/localizations/src/cs-CZ.ts @@ -14,6 +14,12 @@ import type { LocalizationResource } from '@clerk/types'; export const csCZ: LocalizationResource = { locale: 'cs-CZ', + apiKey: { + apiKeyPage: { + formHint: undefined, + title: undefined, + }, + }, backButton: 'Zpět', badge__activePlan: undefined, badge__canceledEndsAt: undefined, diff --git a/packages/localizations/src/da-DK.ts b/packages/localizations/src/da-DK.ts index eb41a9b3505..6cfb69f1fc1 100644 --- a/packages/localizations/src/da-DK.ts +++ b/packages/localizations/src/da-DK.ts @@ -14,6 +14,12 @@ import type { LocalizationResource } from '@clerk/types'; export const daDK: LocalizationResource = { locale: 'da-DK', + apiKey: { + apiKeyPage: { + formHint: undefined, + title: undefined, + }, + }, backButton: 'Tilbage', badge__activePlan: undefined, badge__canceledEndsAt: undefined, diff --git a/packages/localizations/src/de-DE.ts b/packages/localizations/src/de-DE.ts index 52e37d3198b..90793d75cf6 100644 --- a/packages/localizations/src/de-DE.ts +++ b/packages/localizations/src/de-DE.ts @@ -14,6 +14,12 @@ import type { LocalizationResource } from '@clerk/types'; export const deDE: LocalizationResource = { locale: 'de-DE', + apiKey: { + apiKeyPage: { + formHint: undefined, + title: undefined, + }, + }, backButton: 'Zurück', badge__activePlan: 'Aktiv', badge__canceledEndsAt: "Storniert • Endet am {{ date | shortDate('de-DE') }}", diff --git a/packages/localizations/src/el-GR.ts b/packages/localizations/src/el-GR.ts index eca7d49ff10..f203709a3c3 100644 --- a/packages/localizations/src/el-GR.ts +++ b/packages/localizations/src/el-GR.ts @@ -14,6 +14,12 @@ import type { LocalizationResource } from '@clerk/types'; export const elGR: LocalizationResource = { locale: 'el-GR', + apiKey: { + apiKeyPage: { + formHint: undefined, + title: undefined, + }, + }, backButton: 'Πίσω', badge__activePlan: undefined, badge__canceledEndsAt: undefined, diff --git a/packages/localizations/src/en-GB.ts b/packages/localizations/src/en-GB.ts index d0474f6199e..f81ad1fbe78 100644 --- a/packages/localizations/src/en-GB.ts +++ b/packages/localizations/src/en-GB.ts @@ -14,6 +14,12 @@ import type { LocalizationResource } from '@clerk/types'; export const enGB: LocalizationResource = { locale: 'en-GB', + apiKey: { + apiKeyPage: { + formHint: undefined, + title: undefined, + }, + }, backButton: 'Back', badge__activePlan: undefined, badge__canceledEndsAt: undefined, diff --git a/packages/localizations/src/en-US.ts b/packages/localizations/src/en-US.ts index c7da953291e..19b537e7b3c 100644 --- a/packages/localizations/src/en-US.ts +++ b/packages/localizations/src/en-US.ts @@ -2,6 +2,12 @@ import type { LocalizationResource } from '@clerk/types'; export const enUS: LocalizationResource = { locale: 'en-US', + apiKey: { + apiKeyPage: { + formHint: 'Provide a name to generate a new key. You’ll be able to revoke it anytime.', + title: 'Add new API key', + }, + }, backButton: 'Back', badge__activePlan: 'Active', badge__canceledEndsAt: "Canceled • Ends {{ date | shortDate('en-US') }}", diff --git a/packages/localizations/src/es-ES.ts b/packages/localizations/src/es-ES.ts index a78f57221e4..189b1f277fe 100644 --- a/packages/localizations/src/es-ES.ts +++ b/packages/localizations/src/es-ES.ts @@ -14,6 +14,12 @@ import type { LocalizationResource } from '@clerk/types'; export const esES: LocalizationResource = { locale: 'es-ES', + apiKey: { + apiKeyPage: { + formHint: undefined, + title: undefined, + }, + }, backButton: 'Atrás', badge__activePlan: undefined, badge__canceledEndsAt: undefined, diff --git a/packages/localizations/src/es-MX.ts b/packages/localizations/src/es-MX.ts index 07d2d1a76ed..c54d94a4341 100644 --- a/packages/localizations/src/es-MX.ts +++ b/packages/localizations/src/es-MX.ts @@ -14,6 +14,12 @@ import type { LocalizationResource } from '@clerk/types'; export const esMX: LocalizationResource = { locale: 'es-MX', + apiKey: { + apiKeyPage: { + formHint: undefined, + title: undefined, + }, + }, backButton: 'Atrás', badge__activePlan: undefined, badge__canceledEndsAt: undefined, diff --git a/packages/localizations/src/fi-FI.ts b/packages/localizations/src/fi-FI.ts index 5d7c2d70bbc..9b19849c612 100644 --- a/packages/localizations/src/fi-FI.ts +++ b/packages/localizations/src/fi-FI.ts @@ -14,6 +14,12 @@ import type { LocalizationResource } from '@clerk/types'; export const fiFI: LocalizationResource = { locale: 'fi-FI', + apiKey: { + apiKeyPage: { + formHint: undefined, + title: undefined, + }, + }, backButton: 'Takaisin', badge__activePlan: undefined, badge__canceledEndsAt: undefined, diff --git a/packages/localizations/src/fr-FR.ts b/packages/localizations/src/fr-FR.ts index a47a866c18e..f854b99a8d2 100644 --- a/packages/localizations/src/fr-FR.ts +++ b/packages/localizations/src/fr-FR.ts @@ -14,6 +14,12 @@ import type { LocalizationResource } from '@clerk/types'; export const frFR: LocalizationResource = { locale: 'fr-FR', + apiKey: { + apiKeyPage: { + formHint: undefined, + title: undefined, + }, + }, backButton: 'Retour', badge__activePlan: undefined, badge__canceledEndsAt: undefined, diff --git a/packages/localizations/src/he-IL.ts b/packages/localizations/src/he-IL.ts index 53f227c8c6f..462124f72f2 100644 --- a/packages/localizations/src/he-IL.ts +++ b/packages/localizations/src/he-IL.ts @@ -14,6 +14,12 @@ import type { LocalizationResource } from '@clerk/types'; export const heIL: LocalizationResource = { locale: 'he-IL', + apiKey: { + apiKeyPage: { + formHint: undefined, + title: undefined, + }, + }, backButton: 'חזור', badge__activePlan: undefined, badge__canceledEndsAt: undefined, diff --git a/packages/localizations/src/hr-HR.ts b/packages/localizations/src/hr-HR.ts index a6174c7d75d..d4016936773 100644 --- a/packages/localizations/src/hr-HR.ts +++ b/packages/localizations/src/hr-HR.ts @@ -14,6 +14,12 @@ import type { LocalizationResource } from '@clerk/types'; export const hrHR: LocalizationResource = { locale: 'hr-HR', + apiKey: { + apiKeyPage: { + formHint: undefined, + title: undefined, + }, + }, backButton: 'Natrag', badge__activePlan: undefined, badge__canceledEndsAt: undefined, diff --git a/packages/localizations/src/hu-HU.ts b/packages/localizations/src/hu-HU.ts index d4ca76698af..30df0cbc61c 100644 --- a/packages/localizations/src/hu-HU.ts +++ b/packages/localizations/src/hu-HU.ts @@ -14,6 +14,12 @@ import type { LocalizationResource } from '@clerk/types'; export const huHU: LocalizationResource = { locale: 'hu-HU', + apiKey: { + apiKeyPage: { + formHint: undefined, + title: undefined, + }, + }, backButton: 'Vissza', badge__activePlan: undefined, badge__canceledEndsAt: undefined, diff --git a/packages/localizations/src/id-ID.ts b/packages/localizations/src/id-ID.ts index 76cb73b7fc8..11e4efaacc7 100644 --- a/packages/localizations/src/id-ID.ts +++ b/packages/localizations/src/id-ID.ts @@ -14,6 +14,12 @@ import type { LocalizationResource } from '@clerk/types'; export const idID: LocalizationResource = { locale: 'id-ID', + apiKey: { + apiKeyPage: { + formHint: undefined, + title: undefined, + }, + }, backButton: 'Kembali', badge__activePlan: undefined, badge__canceledEndsAt: undefined, diff --git a/packages/localizations/src/is-IS.ts b/packages/localizations/src/is-IS.ts index 0d8eb19acea..bc7d930401e 100644 --- a/packages/localizations/src/is-IS.ts +++ b/packages/localizations/src/is-IS.ts @@ -14,6 +14,12 @@ import type { LocalizationResource } from '@clerk/types'; export const isIS: LocalizationResource = { locale: 'is-IS', + apiKey: { + apiKeyPage: { + formHint: undefined, + title: undefined, + }, + }, backButton: 'Til baka', badge__activePlan: undefined, badge__canceledEndsAt: undefined, diff --git a/packages/localizations/src/it-IT.ts b/packages/localizations/src/it-IT.ts index 3e12d421d91..1a59a63f12d 100644 --- a/packages/localizations/src/it-IT.ts +++ b/packages/localizations/src/it-IT.ts @@ -14,6 +14,12 @@ import type { LocalizationResource } from '@clerk/types'; export const itIT: LocalizationResource = { locale: 'it-IT', + apiKey: { + apiKeyPage: { + formHint: undefined, + title: undefined, + }, + }, backButton: 'Indietro', badge__activePlan: undefined, badge__canceledEndsAt: undefined, diff --git a/packages/localizations/src/ja-JP.ts b/packages/localizations/src/ja-JP.ts index 7c8db99e090..dfa076326c9 100644 --- a/packages/localizations/src/ja-JP.ts +++ b/packages/localizations/src/ja-JP.ts @@ -14,6 +14,12 @@ import type { LocalizationResource } from '@clerk/types'; export const jaJP: LocalizationResource = { locale: 'ja-JP', + apiKey: { + apiKeyPage: { + formHint: undefined, + title: undefined, + }, + }, backButton: '戻る', badge__activePlan: undefined, badge__canceledEndsAt: undefined, diff --git a/packages/localizations/src/ko-KR.ts b/packages/localizations/src/ko-KR.ts index 5be2a1589bc..c3f4672f476 100644 --- a/packages/localizations/src/ko-KR.ts +++ b/packages/localizations/src/ko-KR.ts @@ -14,6 +14,12 @@ import type { LocalizationResource } from '@clerk/types'; export const koKR: LocalizationResource = { locale: 'ko-KR', + apiKey: { + apiKeyPage: { + formHint: undefined, + title: undefined, + }, + }, backButton: '돌아가기', badge__activePlan: undefined, badge__canceledEndsAt: undefined, diff --git a/packages/localizations/src/mn-MN.ts b/packages/localizations/src/mn-MN.ts index f654626dc38..746965aca6d 100644 --- a/packages/localizations/src/mn-MN.ts +++ b/packages/localizations/src/mn-MN.ts @@ -14,6 +14,12 @@ import type { LocalizationResource } from '@clerk/types'; export const mnMN: LocalizationResource = { locale: 'mn-MN', + apiKey: { + apiKeyPage: { + formHint: undefined, + title: undefined, + }, + }, backButton: 'Буцах', badge__activePlan: undefined, badge__canceledEndsAt: undefined, diff --git a/packages/localizations/src/nb-NO.ts b/packages/localizations/src/nb-NO.ts index 0901b07bd46..9326b48a1c2 100644 --- a/packages/localizations/src/nb-NO.ts +++ b/packages/localizations/src/nb-NO.ts @@ -14,6 +14,12 @@ import type { LocalizationResource } from '@clerk/types'; export const nbNO: LocalizationResource = { locale: 'nb-NO', + apiKey: { + apiKeyPage: { + formHint: undefined, + title: undefined, + }, + }, backButton: 'Tilbake', badge__activePlan: undefined, badge__canceledEndsAt: undefined, diff --git a/packages/localizations/src/nl-BE.ts b/packages/localizations/src/nl-BE.ts index 0d7cb47f3c0..78599d9ada1 100644 --- a/packages/localizations/src/nl-BE.ts +++ b/packages/localizations/src/nl-BE.ts @@ -14,6 +14,12 @@ import type { LocalizationResource } from '@clerk/types'; export const nlBE: LocalizationResource = { locale: 'nl-NL', + apiKey: { + apiKeyPage: { + formHint: undefined, + title: undefined, + }, + }, backButton: 'Terug', badge__activePlan: undefined, badge__canceledEndsAt: undefined, diff --git a/packages/localizations/src/nl-NL.ts b/packages/localizations/src/nl-NL.ts index a80832521a7..f3fb9937ed1 100644 --- a/packages/localizations/src/nl-NL.ts +++ b/packages/localizations/src/nl-NL.ts @@ -14,6 +14,12 @@ import type { LocalizationResource } from '@clerk/types'; export const nlNL: LocalizationResource = { locale: 'nl-NL', + apiKey: { + apiKeyPage: { + formHint: undefined, + title: undefined, + }, + }, backButton: 'Terug', badge__activePlan: undefined, badge__canceledEndsAt: undefined, diff --git a/packages/localizations/src/pl-PL.ts b/packages/localizations/src/pl-PL.ts index 96664ac1e60..885e288fce2 100644 --- a/packages/localizations/src/pl-PL.ts +++ b/packages/localizations/src/pl-PL.ts @@ -14,6 +14,12 @@ import type { LocalizationResource } from '@clerk/types'; export const plPL: LocalizationResource = { locale: 'pl-PL', + apiKey: { + apiKeyPage: { + formHint: undefined, + title: undefined, + }, + }, backButton: 'Powrót', badge__activePlan: undefined, badge__canceledEndsAt: undefined, diff --git a/packages/localizations/src/pt-BR.ts b/packages/localizations/src/pt-BR.ts index 39a6e2179ab..1edc78287e3 100644 --- a/packages/localizations/src/pt-BR.ts +++ b/packages/localizations/src/pt-BR.ts @@ -14,6 +14,12 @@ import type { LocalizationResource } from '@clerk/types'; export const ptBR: LocalizationResource = { locale: 'pt-BR', + apiKey: { + apiKeyPage: { + formHint: undefined, + title: undefined, + }, + }, backButton: 'Voltar', badge__activePlan: undefined, badge__canceledEndsAt: undefined, diff --git a/packages/localizations/src/pt-PT.ts b/packages/localizations/src/pt-PT.ts index 6651949c30f..470e2e2c6a5 100644 --- a/packages/localizations/src/pt-PT.ts +++ b/packages/localizations/src/pt-PT.ts @@ -14,6 +14,12 @@ import type { LocalizationResource } from '@clerk/types'; export const ptPT: LocalizationResource = { locale: 'pt-PT', + apiKey: { + apiKeyPage: { + formHint: undefined, + title: undefined, + }, + }, backButton: 'Voltar', badge__activePlan: undefined, badge__canceledEndsAt: undefined, diff --git a/packages/localizations/src/ro-RO.ts b/packages/localizations/src/ro-RO.ts index 57b4a4161ff..5890c96cf8b 100644 --- a/packages/localizations/src/ro-RO.ts +++ b/packages/localizations/src/ro-RO.ts @@ -14,6 +14,12 @@ import type { LocalizationResource } from '@clerk/types'; export const roRO: LocalizationResource = { locale: 'ro-RO', + apiKey: { + apiKeyPage: { + formHint: undefined, + title: undefined, + }, + }, backButton: 'Înapoi', badge__activePlan: undefined, badge__canceledEndsAt: undefined, diff --git a/packages/localizations/src/ru-RU.ts b/packages/localizations/src/ru-RU.ts index cff30a0fb0d..3439d1e4241 100644 --- a/packages/localizations/src/ru-RU.ts +++ b/packages/localizations/src/ru-RU.ts @@ -14,6 +14,12 @@ import type { LocalizationResource } from '@clerk/types'; export const ruRU: LocalizationResource = { locale: 'ru-RU', + apiKey: { + apiKeyPage: { + formHint: undefined, + title: undefined, + }, + }, backButton: 'Назад', badge__activePlan: undefined, badge__canceledEndsAt: undefined, diff --git a/packages/localizations/src/sk-SK.ts b/packages/localizations/src/sk-SK.ts index 3e845b5ce9c..05a230f8281 100644 --- a/packages/localizations/src/sk-SK.ts +++ b/packages/localizations/src/sk-SK.ts @@ -14,6 +14,12 @@ import type { LocalizationResource } from '@clerk/types'; export const skSK: LocalizationResource = { locale: 'sk-SK', + apiKey: { + apiKeyPage: { + formHint: undefined, + title: undefined, + }, + }, backButton: 'Späť', badge__activePlan: undefined, badge__canceledEndsAt: undefined, diff --git a/packages/localizations/src/sr-RS.ts b/packages/localizations/src/sr-RS.ts index 5da9ca35e4c..766af11dabb 100644 --- a/packages/localizations/src/sr-RS.ts +++ b/packages/localizations/src/sr-RS.ts @@ -14,6 +14,12 @@ import type { LocalizationResource } from '@clerk/types'; export const srRS: LocalizationResource = { locale: 'sr-RS', + apiKey: { + apiKeyPage: { + formHint: undefined, + title: undefined, + }, + }, backButton: 'Nazad', badge__activePlan: undefined, badge__canceledEndsAt: undefined, diff --git a/packages/localizations/src/sv-SE.ts b/packages/localizations/src/sv-SE.ts index 89e96349f77..3b3867ce063 100644 --- a/packages/localizations/src/sv-SE.ts +++ b/packages/localizations/src/sv-SE.ts @@ -14,6 +14,12 @@ import type { LocalizationResource } from '@clerk/types'; export const svSE: LocalizationResource = { locale: 'sv-SE', + apiKey: { + apiKeyPage: { + formHint: undefined, + title: undefined, + }, + }, backButton: 'Tillbaka', badge__activePlan: undefined, badge__canceledEndsAt: undefined, diff --git a/packages/localizations/src/th-TH.ts b/packages/localizations/src/th-TH.ts index 8323f645208..5174224fedf 100644 --- a/packages/localizations/src/th-TH.ts +++ b/packages/localizations/src/th-TH.ts @@ -14,6 +14,12 @@ import type { LocalizationResource } from '@clerk/types'; export const thTH: LocalizationResource = { locale: 'th-TH', + apiKey: { + apiKeyPage: { + formHint: undefined, + title: undefined, + }, + }, backButton: 'กลับ', badge__activePlan: undefined, badge__canceledEndsAt: undefined, diff --git a/packages/localizations/src/tr-TR.ts b/packages/localizations/src/tr-TR.ts index 76eab427858..4410c3470b2 100644 --- a/packages/localizations/src/tr-TR.ts +++ b/packages/localizations/src/tr-TR.ts @@ -14,6 +14,12 @@ import type { LocalizationResource } from '@clerk/types'; export const trTR: LocalizationResource = { locale: 'tr-TR', + apiKey: { + apiKeyPage: { + formHint: undefined, + title: undefined, + }, + }, backButton: 'Geri', badge__activePlan: undefined, badge__canceledEndsAt: undefined, diff --git a/packages/localizations/src/uk-UA.ts b/packages/localizations/src/uk-UA.ts index 4d0c9587a40..3d5196629ca 100644 --- a/packages/localizations/src/uk-UA.ts +++ b/packages/localizations/src/uk-UA.ts @@ -14,6 +14,12 @@ import type { LocalizationResource } from '@clerk/types'; export const ukUA: LocalizationResource = { locale: 'uk-UA', + apiKey: { + apiKeyPage: { + formHint: undefined, + title: undefined, + }, + }, backButton: 'Назад', badge__activePlan: undefined, badge__canceledEndsAt: undefined, diff --git a/packages/localizations/src/vi-VN.ts b/packages/localizations/src/vi-VN.ts index 8c7586c5018..e21f8994f87 100644 --- a/packages/localizations/src/vi-VN.ts +++ b/packages/localizations/src/vi-VN.ts @@ -14,6 +14,12 @@ import type { LocalizationResource } from '@clerk/types'; export const viVN: LocalizationResource = { locale: 'vi-VN', + apiKey: { + apiKeyPage: { + formHint: undefined, + title: undefined, + }, + }, backButton: 'Quay lại', badge__activePlan: undefined, badge__canceledEndsAt: undefined, diff --git a/packages/localizations/src/zh-CN.ts b/packages/localizations/src/zh-CN.ts index 754c7e8b268..b32f57d391d 100644 --- a/packages/localizations/src/zh-CN.ts +++ b/packages/localizations/src/zh-CN.ts @@ -14,6 +14,12 @@ import type { LocalizationResource } from '@clerk/types'; export const zhCN: LocalizationResource = { locale: 'zh-CN', + apiKey: { + apiKeyPage: { + formHint: undefined, + title: undefined, + }, + }, backButton: '返回', badge__activePlan: undefined, badge__canceledEndsAt: undefined, diff --git a/packages/localizations/src/zh-TW.ts b/packages/localizations/src/zh-TW.ts index 041380862e8..d3ce726403c 100644 --- a/packages/localizations/src/zh-TW.ts +++ b/packages/localizations/src/zh-TW.ts @@ -14,6 +14,12 @@ import type { LocalizationResource } from '@clerk/types'; export const zhTW: LocalizationResource = { locale: 'zh-TW', + apiKey: { + apiKeyPage: { + formHint: undefined, + title: undefined, + }, + }, backButton: '返回', badge__activePlan: undefined, badge__canceledEndsAt: undefined, diff --git a/packages/types/src/elementIds.ts b/packages/types/src/elementIds.ts index c5cf21acb76..353c4d735dd 100644 --- a/packages/types/src/elementIds.ts +++ b/packages/types/src/elementIds.ts @@ -21,7 +21,8 @@ export type FieldId = | 'enrollmentMode' | 'affiliationEmailAddress' | 'deleteExistingInvitationsSuggestions' - | 'legalAccepted'; + | 'legalAccepted' + | 'description'; export type ProfileSectionId = | 'profile' | 'username' diff --git a/packages/types/src/localization.ts b/packages/types/src/localization.ts index 68869284b34..8b77468b70a 100644 --- a/packages/types/src/localization.ts +++ b/packages/types/src/localization.ts @@ -1040,6 +1040,13 @@ type _LocalizationResource = { message: LocalizationValue; }; }; + apiKey: { + apiKeyPage: { + title: LocalizationValue; + verifyTitle: LocalizationValue; + formHint: LocalizationValue; + }; + }; }; type WithParamName = T & From ae651494f26bb74272de56be9330eb68dbb54c59 Mon Sep 17 00:00:00 2001 From: wobsoriano Date: Thu, 8 May 2025 15:03:29 -0700 Subject: [PATCH 018/116] chore: try refetch --- .../ManageApiKeys/CreateApiKeyForm.tsx | 7 +- .../ManageApiKeys/ManageApiKeys.tsx | 299 +++++++++++------- .../src/ui/elements/contexts/index.tsx | 3 +- 3 files changed, 187 insertions(+), 122 deletions(-) diff --git a/packages/clerk-js/src/ui/components/ManageApiKeys/CreateApiKeyForm.tsx b/packages/clerk-js/src/ui/components/ManageApiKeys/CreateApiKeyForm.tsx index c8b1c9a3911..ad2974038a5 100644 --- a/packages/clerk-js/src/ui/components/ManageApiKeys/CreateApiKeyForm.tsx +++ b/packages/clerk-js/src/ui/components/ManageApiKeys/CreateApiKeyForm.tsx @@ -1,7 +1,7 @@ import React, { useState } from 'react'; import { Button, Flex, localizationKeys } from '../../customizables'; -import { Form, FormButtons, FormContainer, withCardStateProvider } from '../../elements'; +import { Form, FormButtons, FormContainer } from '../../elements'; import { useFormControl } from '../../utils'; interface CreateApiKeyFormProps { @@ -10,7 +10,7 @@ interface CreateApiKeyFormProps { loading?: boolean; } -export const CreateApiKeyForm = withCardStateProvider(({ onCreate, onCancel }: CreateApiKeyFormProps) => { +export const CreateApiKeyForm = ({ onCreate, onCancel }: CreateApiKeyFormProps) => { const [showAdvanced, setShowAdvanced] = useState(false); const nameField = useFormControl('name', '', { @@ -52,6 +52,7 @@ export const CreateApiKeyForm = withCardStateProvider(({ onCreate, onCancel }: C )} + {/* TODO: Add Expiration column */} ); -}); +}; diff --git a/packages/clerk-js/src/ui/components/ManageApiKeys/ManageApiKeys.tsx b/packages/clerk-js/src/ui/components/ManageApiKeys/ManageApiKeys.tsx index 3f4e0ce1cf2..0439d00e74f 100644 --- a/packages/clerk-js/src/ui/components/ManageApiKeys/ManageApiKeys.tsx +++ b/packages/clerk-js/src/ui/components/ManageApiKeys/ManageApiKeys.tsx @@ -2,11 +2,26 @@ import { useClerk } from '@clerk/shared/react'; import { useState } from 'react'; import { useManageApiKeysContext } from '../../contexts'; -import { Box, Button, Flex, Icon, Input, Table, Tbody, Td, Text, Th, Thead, Tr } from '../../customizables'; -import { InputWithIcon, Pagination } from '../../elements'; +import { + Box, + Button, + Col, + Flex, + Flow, + Icon, + Input, + Spinner, + Table, + Tbody, + Td, + Text, + Th, + Thead, + Tr, +} from '../../customizables'; +import { Card, InputWithIcon, Pagination, ThreeDotsMenu, withCardStateProvider } from '../../elements'; import { useClipboard, useFetch } from '../../hooks'; -import { Clipboard, Eye, EyeSlash, MagnifyingGlass, Trash } from '../../icons'; -import { InternalThemeProvider } from '../../styledSystem'; +import { Clipboard, Eye, EyeSlash, MagnifyingGlass } from '../../icons'; import { CreateApiKeyForm } from './CreateApiKeyForm'; const CopyButton = ({ apiKeyID }: { apiKeyID: string }) => { @@ -33,19 +48,32 @@ const CopyButton = ({ apiKeyID }: { apiKeyID: string }) => { ); }; -export const ManageApiKeys = () => { +export const ManageApiKeys = withCardStateProvider(() => { const clerk = useClerk(); const ctx = useManageApiKeysContext(); - const { data: apiKeys, revalidate } = useFetch(() => clerk.getApiKeys({ subject: ctx.subject }), {}); + const { + data: apiKeys, + isLoading, + revalidate, + } = useFetch( + () => clerk.getApiKeys({ subject: ctx.subject }), + { subject: ctx.subject }, + {}, + `api-key-subject-${ctx.subject}`, + ); const [revealedKeys, setRevealedKeys] = useState>({}); const [showCreateForm, setShowCreateForm] = useState(false); const [page, setPage] = useState(1); + const [search, setSearch] = useState(''); const itemsPerPage = ctx.perPage ?? 5; - const itemCount = apiKeys?.length ?? 0; + + const filteredApiKeys = (apiKeys ?? []).filter(key => key.name.toLowerCase().includes(search.toLowerCase())); + + const itemCount = filteredApiKeys.length; const pageCount = Math.max(1, Math.ceil(itemCount / itemsPerPage)); const startingRow = itemCount > 0 ? (page - 1) * itemsPerPage + 1 : 0; const endingRow = Math.min(page * itemsPerPage, itemCount); - const paginatedApiKeys = apiKeys?.slice(startingRow - 1, endingRow) ?? []; + const paginatedApiKeys = filteredApiKeys.slice(startingRow - 1, endingRow); const toggleSecret = async (id: string) => { setRevealedKeys(prev => { @@ -77,116 +105,151 @@ export const ManageApiKeys = () => { }; return ( - - - - } - /> - - - - - {showCreateForm && ( - void handleCreate(params)} - onCancel={() => setShowCreateForm(false)} - /> - )} - - - - - - - - - - - - {paginatedApiKeys.map(apiKey => ( - - - - + + + } + value={search} + onChange={e => { + setSearch(e.target.value); + setPage(1); }} - aria-label='API key (hidden)' - tabIndex={-1} /> - - - - - - ))} - -
    NameLast usedKeyActions
    - {apiKey.name} - - Created at{' '} - {apiKey.createdAt.toLocaleDateString(undefined, { - month: 'short', - day: '2-digit', - year: 'numeric', - })} - - - - {/* Placeholder for "Last used" */} - 3d ago - - - + + +
    - -
    - - -
    + + +
    + + {showCreateForm && ( + void handleCreate(params)} + onCancel={() => setShowCreateForm(false)} + /> + )} + + + + + + + + + + + + {isLoading ? ( + + + + ) : ( + paginatedApiKeys.map(apiKey => ( + + + + + + + )) + )} + +
    NameLast usedKeyActions
    + +
    + {apiKey.name} + + Created at{' '} + {apiKey.createdAt.toLocaleDateString(undefined, { + month: 'short', + day: '2-digit', + year: 'numeric', + })} + + + + 3d ago + + + + + + + + + + + + void revokeApiKey(apiKey.id), + isDisabled: false, + }, + ]} + elementId={'member'} + /> +
    + + + + + + ); -}; +}); diff --git a/packages/clerk-js/src/ui/elements/contexts/index.tsx b/packages/clerk-js/src/ui/elements/contexts/index.tsx index 82a121331ba..afcfb8fb14c 100644 --- a/packages/clerk-js/src/ui/elements/contexts/index.tsx +++ b/packages/clerk-js/src/ui/elements/contexts/index.tsx @@ -86,7 +86,8 @@ export type FlowMetadata = { | 'waitlist' | 'checkout' | 'planDetails' - | 'pricingTable'; + | 'pricingTable' + | 'apiKeys'; part?: | 'start' | 'emailCode' From 4b25f0feb8dbca8a6044c0fca0871d5fa5607686 Mon Sep 17 00:00:00 2001 From: wobsoriano Date: Thu, 8 May 2025 17:53:21 -0700 Subject: [PATCH 019/116] chore: fix revalidation and more styling --- .../ManageApiKeys/CreateApiKeyForm.tsx | 11 +- .../ManageApiKeys/ManageApiKeys.tsx | 108 ++++++++++-------- .../ui/components/UserProfile/ApiKeysPage.tsx | 10 ++ .../UserProfile/UserProfileRoutes.tsx | 14 +++ packages/clerk-js/src/ui/constants.ts | 1 + .../src/ui/utils/createCustomPages.tsx | 8 +- 6 files changed, 98 insertions(+), 54 deletions(-) create mode 100644 packages/clerk-js/src/ui/components/UserProfile/ApiKeysPage.tsx diff --git a/packages/clerk-js/src/ui/components/ManageApiKeys/CreateApiKeyForm.tsx b/packages/clerk-js/src/ui/components/ManageApiKeys/CreateApiKeyForm.tsx index ad2974038a5..464e2ae7e2d 100644 --- a/packages/clerk-js/src/ui/components/ManageApiKeys/CreateApiKeyForm.tsx +++ b/packages/clerk-js/src/ui/components/ManageApiKeys/CreateApiKeyForm.tsx @@ -2,16 +2,16 @@ import React, { useState } from 'react'; import { Button, Flex, localizationKeys } from '../../customizables'; import { Form, FormButtons, FormContainer } from '../../elements'; +import { useActionContext } from '../../elements/Action/ActionRoot'; import { useFormControl } from '../../utils'; interface CreateApiKeyFormProps { - onCreate: (params: { name: string; description?: string; expiration?: number }) => void; - onCancel: () => void; - loading?: boolean; + onCreate: (params: { name: string; description?: string; expiration?: number; closeFn: () => void }) => void; } -export const CreateApiKeyForm = ({ onCreate, onCancel }: CreateApiKeyFormProps) => { +export const CreateApiKeyForm = ({ onCreate }: CreateApiKeyFormProps) => { const [showAdvanced, setShowAdvanced] = useState(false); + const { close } = useActionContext(); const nameField = useFormControl('name', '', { type: 'text', @@ -35,6 +35,7 @@ export const CreateApiKeyForm = ({ onCreate, onCancel }: CreateApiKeyFormProps) name: nameField.value, description: descriptionField.value || undefined, expiration: undefined, + closeFn: close, }); }; @@ -65,7 +66,7 @@ export const CreateApiKeyForm = ({ onCreate, onCancel }: CreateApiKeyFormProps) diff --git a/packages/clerk-js/src/ui/components/ManageApiKeys/ManageApiKeys.tsx b/packages/clerk-js/src/ui/components/ManageApiKeys/ManageApiKeys.tsx index 0439d00e74f..776155f8db4 100644 --- a/packages/clerk-js/src/ui/components/ManageApiKeys/ManageApiKeys.tsx +++ b/packages/clerk-js/src/ui/components/ManageApiKeys/ManageApiKeys.tsx @@ -20,6 +20,7 @@ import { Tr, } from '../../customizables'; import { Card, InputWithIcon, Pagination, ThreeDotsMenu, withCardStateProvider } from '../../elements'; +import { Action } from '../../elements/Action'; import { useClipboard, useFetch } from '../../hooks'; import { Clipboard, Eye, EyeSlash, MagnifyingGlass } from '../../icons'; import { CreateApiKeyForm } from './CreateApiKeyForm'; @@ -55,14 +56,8 @@ export const ManageApiKeys = withCardStateProvider(() => { data: apiKeys, isLoading, revalidate, - } = useFetch( - () => clerk.getApiKeys({ subject: ctx.subject }), - { subject: ctx.subject }, - {}, - `api-key-subject-${ctx.subject}`, - ); + } = useFetch(clerk.getApiKeys, { subject: ctx.subject }, undefined, `api-key-source-${ctx.subject}`); const [revealedKeys, setRevealedKeys] = useState>({}); - const [showCreateForm, setShowCreateForm] = useState(false); const [page, setPage] = useState(1); const [search, setSearch] = useState(''); const itemsPerPage = ctx.perPage ?? 5; @@ -89,18 +84,24 @@ export const ManageApiKeys = withCardStateProvider(() => { } }; - const handleCreate = async (params: { name: string; description?: string; expiration?: number }) => { + const handleCreate = async (params: { + name: string; + description?: string; + expiration?: number; + closeFn: () => void; + }) => { await clerk.createApiKey({ name: params.name, creationReason: params.description, secondsUntilExpiration: params.expiration, }); - setShowCreateForm(false); + params.closeFn(); revalidate(); }; const revokeApiKey = async (apiKeyID: string) => { await clerk.revokeApiKey({ apiKeyID, revocationReason: 'Revoked by user' }); + setPage(1); revalidate(); }; @@ -109,35 +110,44 @@ export const ManageApiKeys = withCardStateProvider(() => { - - - } - value={search} - onChange={e => { - setSearch(e.target.value); - setPage(1); - }} - /> - - - - - {showCreateForm && ( - void handleCreate(params)} - onCancel={() => setShowCreateForm(false)} - /> - )} + + } + value={search} + onChange={e => { + setSearch(e.target.value); + setPage(1); + }} + /> + + + + + + + + ({ + paddingTop: t.space.$6, + paddingBottom: t.space.$6, + })} + > + + void handleCreate(params)} /> + + + + @@ -236,17 +246,19 @@ export const ManageApiKeys = withCardStateProvider(() => {
    - + {itemCount > 5 && ( + + )}
    diff --git a/packages/clerk-js/src/ui/components/UserProfile/ApiKeysPage.tsx b/packages/clerk-js/src/ui/components/UserProfile/ApiKeysPage.tsx new file mode 100644 index 00000000000..92ea2318a7a --- /dev/null +++ b/packages/clerk-js/src/ui/components/UserProfile/ApiKeysPage.tsx @@ -0,0 +1,10 @@ +import { ManageApiKeysContext } from '../../contexts'; +import { ManageApiKeys } from '../ManageApiKeys'; + +export const ApiKeysPage = () => { + return ( + + + + ); +}; diff --git a/packages/clerk-js/src/ui/components/UserProfile/UserProfileRoutes.tsx b/packages/clerk-js/src/ui/components/UserProfile/UserProfileRoutes.tsx index 6252af0e006..6017c520938 100644 --- a/packages/clerk-js/src/ui/components/UserProfile/UserProfileRoutes.tsx +++ b/packages/clerk-js/src/ui/components/UserProfile/UserProfileRoutes.tsx @@ -15,6 +15,12 @@ const BillingPage = lazy(() => })), ); +const ApiKeysPage = lazy(() => + import(/* webpackChunkName: "up-api-key-page"*/ './ApiKeysPage').then(module => ({ + default: module.ApiKeysPage, + })), +); + export const UserProfileRoutes = () => { const { pages } = useUserProfileContext(); const { commerceSettings } = useEnvironment(); @@ -22,6 +28,7 @@ export const UserProfileRoutes = () => { 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 customPageRoutesWithContents = pages.contents?.map((customPage, index) => { const shouldFirstCustomItemBeOnRoot = !isAccountPageRoot && !isSecurityPageRoot && index === 0; @@ -80,6 +87,13 @@ export const UserProfileRoutes = () => { )} + + + + + + + ); diff --git a/packages/clerk-js/src/ui/constants.ts b/packages/clerk-js/src/ui/constants.ts index 0dc8bc2a1cc..5be19ae5358 100644 --- a/packages/clerk-js/src/ui/constants.ts +++ b/packages/clerk-js/src/ui/constants.ts @@ -2,6 +2,7 @@ export const USER_PROFILE_NAVBAR_ROUTE_ID = { ACCOUNT: 'account', SECURITY: 'security', BILLING: 'billing', + API_KEYS: 'apiKeys', }; export const ORGANIZATION_PROFILE_NAVBAR_ROUTE_ID = { diff --git a/packages/clerk-js/src/ui/utils/createCustomPages.tsx b/packages/clerk-js/src/ui/utils/createCustomPages.tsx index b92be852602..87b683ab0b2 100644 --- a/packages/clerk-js/src/ui/utils/createCustomPages.tsx +++ b/packages/clerk-js/src/ui/utils/createCustomPages.tsx @@ -3,7 +3,7 @@ import type { CustomPage, EnvironmentResource, LoadedClerk } from '@clerk/types' import { disabledBillingFeature, hasPaidOrgPlans, hasPaidUserPlans, isValidUrl } from '../../utils'; import { ORGANIZATION_PROFILE_NAVBAR_ROUTE_ID, USER_PROFILE_NAVBAR_ROUTE_ID } from '../constants'; import type { NavbarRoute } from '../elements/Navbar'; -import { CreditCard, Organization, TickShield, User, Users } from '../icons'; +import { CreditCard, InformationCircle,Organization, TickShield, User, Users } from '../icons'; import { localizationKeys } from '../localization'; import { ExternalElementMounter } from './ExternalElementMounter'; import { isDevelopmentSDK } from './runtimeEnvironment'; @@ -259,6 +259,12 @@ const getUserProfileDefaultRoutes = ({ commerce }: { commerce: boolean }): GetDe icon: TickShield, path: 'security', }, + { + name: 'API keys', + id: USER_PROFILE_NAVBAR_ROUTE_ID.API_KEYS, + icon: InformationCircle, + path: 'api-keys', + }, ]; if (commerce) { INITIAL_ROUTES.push({ From 69925f7951929fdb07c3d485ffd17fb215fce2f5 Mon Sep 17 00:00:00 2001 From: wobsoriano Date: Thu, 8 May 2025 18:32:45 -0700 Subject: [PATCH 020/116] chore: rename component to --- packages/clerk-js/sandbox/app.ts | 8 ++++---- packages/clerk-js/src/core/clerk.ts | 14 ++++++------- .../ManageApiKeys.tsx => ApiKeys/ApiKeys.tsx} | 6 +++--- .../CreateApiKeyForm.tsx | 0 .../src/ui/components/ApiKeys/index.tsx | 1 + .../src/ui/components/ManageApiKeys/index.tsx | 1 - .../ui/components/UserProfile/ApiKeysPage.tsx | 10 +++++----- .../ui/contexts/ClerkUIComponentsContext.tsx | 10 +++++----- .../src/ui/contexts/components/ApiKeys.ts | 20 +++++++++++++++++++ .../ui/contexts/components/ManageApiKeys.ts | 20 ------------------- .../src/ui/contexts/components/index.ts | 2 +- .../clerk-js/src/ui/lazyModules/components.ts | 8 +++----- packages/clerk-js/src/ui/types.ts | 10 +++++----- packages/react/src/components/index.ts | 2 +- .../react/src/components/uiComponents.tsx | 12 +++++------ packages/react/src/isomorphicClerk.ts | 20 +++++++++---------- packages/types/src/appearance.ts | 4 ++-- packages/types/src/clerk.ts | 18 ++++++++--------- 18 files changed, 82 insertions(+), 84 deletions(-) rename packages/clerk-js/src/ui/components/{ManageApiKeys/ManageApiKeys.tsx => ApiKeys/ApiKeys.tsx} (98%) rename packages/clerk-js/src/ui/components/{ManageApiKeys => ApiKeys}/CreateApiKeyForm.tsx (100%) create mode 100644 packages/clerk-js/src/ui/components/ApiKeys/index.tsx delete mode 100644 packages/clerk-js/src/ui/components/ManageApiKeys/index.tsx create mode 100644 packages/clerk-js/src/ui/contexts/components/ApiKeys.ts delete mode 100644 packages/clerk-js/src/ui/contexts/components/ManageApiKeys.ts diff --git a/packages/clerk-js/sandbox/app.ts b/packages/clerk-js/sandbox/app.ts index 4fde3c997bf..8aa960f224e 100644 --- a/packages/clerk-js/sandbox/app.ts +++ b/packages/clerk-js/sandbox/app.ts @@ -33,7 +33,7 @@ const AVAILABLE_COMPONENTS = [ 'organizationSwitcher', 'waitlist', 'pricingTable', - 'manageApiKeys', + 'apiKeys', ] as const; const COMPONENT_PROPS_NAMESPACE = 'clerk-js-sandbox'; @@ -92,7 +92,7 @@ const componentControls: Record<(typeof AVAILABLE_COMPONENTS)[number], Component organizationSwitcher: buildComponentControls('organizationSwitcher'), waitlist: buildComponentControls('waitlist'), pricingTable: buildComponentControls('pricingTable'), - manageApiKeys: buildComponentControls('manageApiKeys'), + apiKeys: buildComponentControls('apiKeys'), }; declare global { @@ -312,8 +312,8 @@ void (async () => { '/pricing-table': () => { Clerk.mountPricingTable(app, componentControls.pricingTable.getProps() ?? {}); }, - '/manage-api-keys': () => { - Clerk.mountManageApiKeys(app, componentControls.manageApiKeys.getProps() ?? {}); + '/api-keys': () => { + Clerk.mountApiKeys(app, componentControls.apiKeys.getProps() ?? {}); }, '/open-sign-in': () => { mountOpenSignInButton(app, componentControls.signIn.getProps() ?? {}); diff --git a/packages/clerk-js/src/core/clerk.ts b/packages/clerk-js/src/core/clerk.ts index 6adf308614b..c1dce145600 100644 --- a/packages/clerk-js/src/core/clerk.ts +++ b/packages/clerk-js/src/core/clerk.ts @@ -20,6 +20,7 @@ import type { __internal_PlanDetailsProps, __internal_UserVerificationModalProps, ApiKeyResource, + ApiKeysProps, AuthenticateWithCoinbaseWalletParams, AuthenticateWithGoogleOneTapParams, AuthenticateWithMetamaskParams, @@ -46,7 +47,6 @@ import type { InstanceType, JoinWaitlistParams, ListenerCallback, - ManageApiKeysProps, NavigateOptions, NextTaskParams, OrganizationListProps, @@ -1043,21 +1043,21 @@ export class Clerk implements ClerkInterface { ); }; - public mountManageApiKeys = (node: HTMLDivElement, props?: ManageApiKeysProps): void => { + public mountApiKeys = (node: HTMLDivElement, props?: ApiKeysProps): void => { this.assertComponentsReady(this.#componentControls); - void this.#componentControls.ensureMounted({ preloadHint: 'ManageApiKeys' }).then(controls => + void this.#componentControls.ensureMounted({ preloadHint: 'ApiKeys' }).then(controls => controls.mountComponent({ - name: 'ManageApiKeys', - appearanceKey: 'manageApiKeys', + name: 'ApiKeys', + appearanceKey: 'apiKeys', node, props, }), ); - this.telemetry?.record(eventPrebuiltComponentMounted('ManageApiKeys', props)); + this.telemetry?.record(eventPrebuiltComponentMounted('ApiKeys', props)); }; - public unmountManageApiKeys = (node: HTMLDivElement): void => { + public unmountApiKeys = (node: HTMLDivElement): void => { this.assertComponentsReady(this.#componentControls); void this.#componentControls.ensureMounted().then(controls => controls.unmountComponent({ diff --git a/packages/clerk-js/src/ui/components/ManageApiKeys/ManageApiKeys.tsx b/packages/clerk-js/src/ui/components/ApiKeys/ApiKeys.tsx similarity index 98% rename from packages/clerk-js/src/ui/components/ManageApiKeys/ManageApiKeys.tsx rename to packages/clerk-js/src/ui/components/ApiKeys/ApiKeys.tsx index 776155f8db4..fdf69ba6090 100644 --- a/packages/clerk-js/src/ui/components/ManageApiKeys/ManageApiKeys.tsx +++ b/packages/clerk-js/src/ui/components/ApiKeys/ApiKeys.tsx @@ -1,7 +1,7 @@ import { useClerk } from '@clerk/shared/react'; import { useState } from 'react'; -import { useManageApiKeysContext } from '../../contexts'; +import { useApiKeysContext } from '../../contexts'; import { Box, Button, @@ -49,9 +49,9 @@ const CopyButton = ({ apiKeyID }: { apiKeyID: string }) => { ); }; -export const ManageApiKeys = withCardStateProvider(() => { +export const ApiKeys = withCardStateProvider(() => { const clerk = useClerk(); - const ctx = useManageApiKeysContext(); + const ctx = useApiKeysContext(); const { data: apiKeys, isLoading, diff --git a/packages/clerk-js/src/ui/components/ManageApiKeys/CreateApiKeyForm.tsx b/packages/clerk-js/src/ui/components/ApiKeys/CreateApiKeyForm.tsx similarity index 100% rename from packages/clerk-js/src/ui/components/ManageApiKeys/CreateApiKeyForm.tsx rename to packages/clerk-js/src/ui/components/ApiKeys/CreateApiKeyForm.tsx diff --git a/packages/clerk-js/src/ui/components/ApiKeys/index.tsx b/packages/clerk-js/src/ui/components/ApiKeys/index.tsx new file mode 100644 index 00000000000..a83ede2a3d7 --- /dev/null +++ b/packages/clerk-js/src/ui/components/ApiKeys/index.tsx @@ -0,0 +1 @@ +export * from './ApiKeys'; diff --git a/packages/clerk-js/src/ui/components/ManageApiKeys/index.tsx b/packages/clerk-js/src/ui/components/ManageApiKeys/index.tsx deleted file mode 100644 index 1cd1c4ab7df..00000000000 --- a/packages/clerk-js/src/ui/components/ManageApiKeys/index.tsx +++ /dev/null @@ -1 +0,0 @@ -export * from './ManageApiKeys'; diff --git a/packages/clerk-js/src/ui/components/UserProfile/ApiKeysPage.tsx b/packages/clerk-js/src/ui/components/UserProfile/ApiKeysPage.tsx index 92ea2318a7a..351f0316505 100644 --- a/packages/clerk-js/src/ui/components/UserProfile/ApiKeysPage.tsx +++ b/packages/clerk-js/src/ui/components/UserProfile/ApiKeysPage.tsx @@ -1,10 +1,10 @@ -import { ManageApiKeysContext } from '../../contexts'; -import { ManageApiKeys } from '../ManageApiKeys'; +import { ApiKeysContext } from '../../contexts'; +import { ApiKeys } from '../ApiKeys'; export const ApiKeysPage = () => { return ( - - - + + + ); }; diff --git a/packages/clerk-js/src/ui/contexts/ClerkUIComponentsContext.tsx b/packages/clerk-js/src/ui/contexts/ClerkUIComponentsContext.tsx index f788f2f3412..7290a3c75f9 100644 --- a/packages/clerk-js/src/ui/contexts/ClerkUIComponentsContext.tsx +++ b/packages/clerk-js/src/ui/contexts/ClerkUIComponentsContext.tsx @@ -1,11 +1,11 @@ -import type { ManageApiKeysProps, PricingTableProps, UserButtonProps, WaitlistProps } from '@clerk/types'; +import type { ApiKeysProps, PricingTableProps, UserButtonProps, WaitlistProps } from '@clerk/types'; import type { ReactNode } from 'react'; import type { AvailableComponentName, AvailableComponentProps } from '../types'; import { + ApiKeysContext, CreateOrganizationContext, GoogleOneTapContext, - ManageApiKeysContext, OrganizationListContext, OrganizationProfileContext, OrganizationSwitcherContext, @@ -89,11 +89,11 @@ export function ComponentContextProvider({ ); - case 'ManageApiKeys': + case 'ApiKeys': return ( - + {children} - + ); default: diff --git a/packages/clerk-js/src/ui/contexts/components/ApiKeys.ts b/packages/clerk-js/src/ui/contexts/components/ApiKeys.ts new file mode 100644 index 00000000000..e1823898d83 --- /dev/null +++ b/packages/clerk-js/src/ui/contexts/components/ApiKeys.ts @@ -0,0 +1,20 @@ +import { createContext, useContext } from 'react'; + +import type { ApiKeysCtx } from '../../types'; + +export const ApiKeysContext = createContext(null); + +export const useApiKeysContext = () => { + const context = useContext(ApiKeysContext); + + if (!context || context.componentName !== 'ApiKeys') { + throw new Error('Clerk: useApiKeysContext called outside ApiKeys.'); + } + + const { componentName, ...ctx } = context; + + return { + ...ctx, + componentName, + }; +}; diff --git a/packages/clerk-js/src/ui/contexts/components/ManageApiKeys.ts b/packages/clerk-js/src/ui/contexts/components/ManageApiKeys.ts deleted file mode 100644 index e3ee7fcc172..00000000000 --- a/packages/clerk-js/src/ui/contexts/components/ManageApiKeys.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { createContext, useContext } from 'react'; - -import type { ManageApiKeysCtx } from '../../types'; - -export const ManageApiKeysContext = createContext(null); - -export const useManageApiKeysContext = () => { - const context = useContext(ManageApiKeysContext); - - if (!context || context.componentName !== 'ManageApiKeys') { - throw new Error('Clerk: useManageApiKeysContext called outside ManageApiKeys.'); - } - - const { componentName, ...ctx } = context; - - return { - ...ctx, - componentName, - }; -}; diff --git a/packages/clerk-js/src/ui/contexts/components/index.ts b/packages/clerk-js/src/ui/contexts/components/index.ts index 6f9e5a2abf2..f8674b6a932 100644 --- a/packages/clerk-js/src/ui/contexts/components/index.ts +++ b/packages/clerk-js/src/ui/contexts/components/index.ts @@ -15,4 +15,4 @@ export * from './PricingTable'; export * from './Checkout'; export * from './Statements'; export * from './Plans'; -export * from './ManageApiKeys'; +export * from './ApiKeys'; diff --git a/packages/clerk-js/src/ui/lazyModules/components.ts b/packages/clerk-js/src/ui/lazyModules/components.ts index f3fbf423d07..edb02b887ca 100644 --- a/packages/clerk-js/src/ui/lazyModules/components.ts +++ b/packages/clerk-js/src/ui/lazyModules/components.ts @@ -21,7 +21,7 @@ const componentImportPaths = { Checkout: () => import(/* webpackChunkName: "checkout" */ '../components/Checkout'), SessionTasks: () => import(/* webpackChunkName: "sessionTasks" */ '../components/SessionTasks'), PlanDetails: () => import(/* webpackChunkName: "planDetails" */ '../components/Plans'), - ManageApiKeys: () => import(/* webpackChunkName: "manageApiKeys" */ '../components/ManageApiKeys'), + ApiKeys: () => import(/* webpackChunkName: "apiKeys" */ '../components/ApiKeys'), } as const; export const SignIn = lazy(() => componentImportPaths.SignIn().then(module => ({ default: module.SignIn }))); @@ -97,9 +97,7 @@ export const PricingTable = lazy(() => componentImportPaths.PricingTable().then(module => ({ default: module.PricingTable })), ); -export const ManageApiKeys = lazy(() => - componentImportPaths.ManageApiKeys().then(module => ({ default: module.ManageApiKeys })), -); +export const ApiKeys = lazy(() => componentImportPaths.ApiKeys().then(module => ({ default: module.ApiKeys }))); export const Checkout = lazy(() => componentImportPaths.Checkout().then(module => ({ default: module.Checkout }))); @@ -138,7 +136,7 @@ export const ClerkComponents = { PricingTable, Checkout, PlanDetails, - ManageApiKeys, + ApiKeys, }; export type ClerkComponentName = keyof typeof ClerkComponents; diff --git a/packages/clerk-js/src/ui/types.ts b/packages/clerk-js/src/ui/types.ts index 346d3bf8f35..5c925782846 100644 --- a/packages/clerk-js/src/ui/types.ts +++ b/packages/clerk-js/src/ui/types.ts @@ -2,9 +2,9 @@ import type { __internal_CheckoutProps, __internal_PlanDetailsProps, __internal_UserVerificationProps, + APIKeysProps, CreateOrganizationProps, GoogleOneTapProps, - ManageApiKeysProps, NewSubscriptionRedirectUrl, OrganizationListProps, OrganizationProfileProps, @@ -49,7 +49,7 @@ export type AvailableComponentProps = | __internal_CheckoutProps | __internal_UserVerificationProps | __internal_PlanDetailsProps - | ManageApiKeysProps; + | APIKeysProps; type ComponentMode = 'modal' | 'mounted'; @@ -116,8 +116,8 @@ export type PricingTableCtx = PricingTableProps & { mode?: ComponentMode; }; -export type ManageApiKeysCtx = ManageApiKeysProps & { - componentName: 'ManageApiKeys'; +export type ApiKeysCtx = APIKeysProps & { + componentName: 'ApiKeys'; mode?: ComponentMode; }; @@ -144,6 +144,6 @@ export type AvailableComponentCtx = | WaitlistCtx | PricingTableCtx | CheckoutCtx - | ManageApiKeysCtx; + | ApiKeysCtx; export type AvailableComponentName = AvailableComponentCtx['componentName']; diff --git a/packages/react/src/components/index.ts b/packages/react/src/components/index.ts index 4b988ca6f0a..263227ee084 100644 --- a/packages/react/src/components/index.ts +++ b/packages/react/src/components/index.ts @@ -10,7 +10,7 @@ export { GoogleOneTap, Waitlist, PricingTable, - ManageApiKeys, + ApiKeys, } from './uiComponents'; export { diff --git a/packages/react/src/components/uiComponents.tsx b/packages/react/src/components/uiComponents.tsx index 4c65ecba6b8..7ec3dc7f09d 100644 --- a/packages/react/src/components/uiComponents.tsx +++ b/packages/react/src/components/uiComponents.tsx @@ -1,8 +1,8 @@ import { logErrorInDevMode } from '@clerk/shared/utils'; import type { + ApiKeysProps, CreateOrganizationProps, GoogleOneTapProps, - ManageApiKeysProps, OrganizationListProps, OrganizationProfileProps, OrganizationSwitcherProps, @@ -602,8 +602,8 @@ export const PricingTable = withClerk( { component: 'PricingTable', renderWhileLoading: true }, ); -export const ManageApiKeys = withClerk( - ({ clerk, component, fallback, ...props }: WithClerkProp) => { +export const ApiKeys = withClerk( + ({ clerk, component, fallback, ...props }: WithClerkProp) => { const mountingStatus = useWaitForComponentMount(component); const shouldShowFallback = mountingStatus === 'rendering' || !clerk.loaded; @@ -617,8 +617,8 @@ export const ManageApiKeys = withClerk( {clerk.loaded && ( ); }, - { component: 'ManageApiKeys', renderWhileLoading: true }, + { component: 'ApiKeys', renderWhileLoading: true }, ); diff --git a/packages/react/src/isomorphicClerk.ts b/packages/react/src/isomorphicClerk.ts index 7b8c3255b5e..b3a7b2db84f 100644 --- a/packages/react/src/isomorphicClerk.ts +++ b/packages/react/src/isomorphicClerk.ts @@ -8,6 +8,7 @@ import type { __internal_UserVerificationModalProps, __internal_UserVerificationProps, ApiKeyResource, + ApiKeysProps, AuthenticateWithCoinbaseWalletParams, AuthenticateWithGoogleOneTapParams, AuthenticateWithMetamaskParams, @@ -29,7 +30,6 @@ import type { JoinWaitlistParams, ListenerCallback, LoadedClerk, - ManageApiKeysProps, NextTaskParams, OrganizationListProps, OrganizationProfileProps, @@ -136,7 +136,7 @@ export class IsomorphicClerk implements IsomorphicLoadedClerk { private premountMethodCalls = new Map, MethodCallback>(); private premountWaitlistNodes = new Map(); private premountPricingTableNodes = new Map(); - private premountManageApiKeysNodes = new Map(); + private premountApiKeysNodes = new Map(); // A separate Map of `addListener` method calls to handle multiple listeners. private premountAddListenerCalls = new Map< ListenerCallback, @@ -615,8 +615,8 @@ export class IsomorphicClerk implements IsomorphicLoadedClerk { clerkjs.mountPricingTable(node, props); }); - this.premountManageApiKeysNodes.forEach((props, node) => { - clerkjs.mountManageApiKeys(node, props); + this.premountApiKeysNodes.forEach((props, node) => { + clerkjs.mountApiKeys(node, props); }); /** @@ -1060,19 +1060,19 @@ export class IsomorphicClerk implements IsomorphicLoadedClerk { } }; - mountManageApiKeys = (node: HTMLDivElement, props?: ManageApiKeysProps): void => { + mountApiKeys = (node: HTMLDivElement, props?: ApiKeysProps): void => { if (this.clerkjs && this.loaded) { - this.clerkjs.mountManageApiKeys(node, props); + this.clerkjs.mountApiKeys(node, props); } else { - this.premountManageApiKeysNodes.set(node, props); + this.premountApiKeysNodes.set(node, props); } }; - unmountManageApiKeys = (node: HTMLDivElement): void => { + unmountApiKeys = (node: HTMLDivElement): void => { if (this.clerkjs && this.loaded) { - this.clerkjs.unmountManageApiKeys(node); + this.clerkjs.unmountApiKeys(node); } else { - this.premountManageApiKeysNodes.delete(node); + this.premountApiKeysNodes.delete(node); } }; diff --git a/packages/types/src/appearance.ts b/packages/types/src/appearance.ts index c7a1656ee04..7956366761b 100644 --- a/packages/types/src/appearance.ts +++ b/packages/types/src/appearance.ts @@ -899,7 +899,7 @@ export type Appearance = T & */ checkout?: T; /** - * Theme overrides that only apply to the `` component + * Theme overrides that only apply to the `` component */ - manageApiKeys?: T; + apiKeys?: T; }; diff --git a/packages/types/src/clerk.ts b/packages/types/src/clerk.ts index 97e427f5143..b729b2cf2f1 100644 --- a/packages/types/src/clerk.ts +++ b/packages/types/src/clerk.ts @@ -1,9 +1,9 @@ import type { ApiKeyResource } from './apiKey'; import type { + ApiKeysTheme, Appearance, CheckoutTheme, CreateOrganizationTheme, - ManageApiKeysTheme, OrganizationListTheme, OrganizationProfileTheme, OrganizationSwitcherTheme, @@ -461,19 +461,19 @@ export interface Clerk { unmountPricingTable: (targetNode: HTMLDivElement) => void; /** - * Mount a manage api keys component at the target element. - * @param targetNode Target to mount the ManageApiKeys component. + * Mount a api keys component at the target element. + * @param targetNode Target to mount the ApiKeys component. * @param props Configuration parameters. */ - mountManageApiKeys: (targetNode: HTMLDivElement, props?: ManageApiKeysProps) => void; + mountApiKeys: (targetNode: HTMLDivElement, props?: ApiKeysProps) => void; /** - * Unmount a manage api keys component from the target element. + * Unmount a api keys component from the target element. * If there is no component mounted at the target node, results in a noop. * - * @param targetNode Target node to unmount the ManageApiKeys component from. + * @param targetNode Target node to unmount the ApiKeys component from. */ - unmountManageApiKeys: (targetNode: HTMLDivElement) => void; + unmountApiKeys: (targetNode: HTMLDivElement) => void; /** * Register a listener that triggers a callback each time important Clerk resources are changed. @@ -1658,12 +1658,12 @@ type PortalRoot = HTMLElement | null | undefined; export type PricingTableProps = PricingTableBaseProps & PricingTableDefaultProps; -export type ManageApiKeysProps = { +export type ApiKeysProps = { type?: 'api_key'; subject?: string; perPage?: number; showSecretByDefault?: boolean; - appearance?: ManageApiKeysTheme; + appearance?: ApiKeysTheme; }; export type GetApiKeysParams = { From 8de82197b0bc6dc109d5a9f8aeb479f05af1de74 Mon Sep 17 00:00:00 2001 From: wobsoriano Date: Thu, 8 May 2025 19:47:58 -0700 Subject: [PATCH 021/116] chore: add expiration field --- .../src/ui/components/ApiKeys/ApiKeys.tsx | 32 +++++++++-- .../components/ApiKeys/CreateApiKeyForm.tsx | 54 ++++++++++++++++--- 2 files changed, 73 insertions(+), 13 deletions(-) diff --git a/packages/clerk-js/src/ui/components/ApiKeys/ApiKeys.tsx b/packages/clerk-js/src/ui/components/ApiKeys/ApiKeys.tsx index fdf69ba6090..7b890c1f4e0 100644 --- a/packages/clerk-js/src/ui/components/ApiKeys/ApiKeys.tsx +++ b/packages/clerk-js/src/ui/components/ApiKeys/ApiKeys.tsx @@ -23,7 +23,7 @@ import { Card, InputWithIcon, Pagination, ThreeDotsMenu, withCardStateProvider } import { Action } from '../../elements/Action'; import { useClipboard, useFetch } from '../../hooks'; import { Clipboard, Eye, EyeSlash, MagnifyingGlass } from '../../icons'; -import { CreateApiKeyForm } from './CreateApiKeyForm'; +import { CreateApiKeyForm, Expiration } from './CreateApiKeyForm'; const CopyButton = ({ apiKeyID }: { apiKeyID: string }) => { const clerk = useClerk(); @@ -49,6 +49,28 @@ const CopyButton = ({ apiKeyID }: { apiKeyID: string }) => { ); }; +function getTimeLeftInSeconds(expirationOption: Expiration) { + if (expirationOption === 'never') { + return; + } + + const now = new Date(); + const future = new Date(now); + + if (expirationOption === '30d') { + future.setDate(future.getDate() + 30); + } else if (expirationOption === '90d') { + future.setDate(future.getDate() + 90); + } else { + throw new Error('TODO: Improve time handling'); + } + + const diffInMs = future.getTime() - now.getTime(); + const diffInSecs = Math.floor(diffInMs / 1000); + + return diffInSecs; +} + export const ApiKeys = withCardStateProvider(() => { const clerk = useClerk(); const ctx = useApiKeysContext(); @@ -87,13 +109,13 @@ export const ApiKeys = withCardStateProvider(() => { const handleCreate = async (params: { name: string; description?: string; - expiration?: number; + expiration: Expiration; closeFn: () => void; }) => { await clerk.createApiKey({ name: params.name, creationReason: params.description, - secondsUntilExpiration: params.expiration, + secondsUntilExpiration: getTimeLeftInSeconds(params.expiration), }); params.closeFn(); revalidate(); @@ -230,8 +252,8 @@ export const ApiKeys = withCardStateProvider(() => { void revokeApiKey(apiKey.id), isDisabled: false, diff --git a/packages/clerk-js/src/ui/components/ApiKeys/CreateApiKeyForm.tsx b/packages/clerk-js/src/ui/components/ApiKeys/CreateApiKeyForm.tsx index 464e2ae7e2d..bef9c08c188 100644 --- a/packages/clerk-js/src/ui/components/ApiKeys/CreateApiKeyForm.tsx +++ b/packages/clerk-js/src/ui/components/ApiKeys/CreateApiKeyForm.tsx @@ -1,17 +1,22 @@ import React, { useState } from 'react'; -import { Button, Flex, localizationKeys } from '../../customizables'; -import { Form, FormButtons, FormContainer } from '../../elements'; +import { Button, Col, Flex, localizationKeys, Text } from '../../customizables'; +import { Form, FormButtons, FormContainer, SegmentedControl } from '../../elements'; import { useActionContext } from '../../elements/Action/ActionRoot'; import { useFormControl } from '../../utils'; interface CreateApiKeyFormProps { - onCreate: (params: { name: string; description?: string; expiration?: number; closeFn: () => void }) => void; + onCreate: (params: { name: string; description?: string; expiration: Expiration; closeFn: () => void }) => void; } +export type Expiration = 'never' | '30d' | '90d' | 'custom'; + export const CreateApiKeyForm = ({ onCreate }: CreateApiKeyFormProps) => { const [showAdvanced, setShowAdvanced] = useState(false); const { close } = useActionContext(); + const [expiration, setExpiration] = useState('never'); + const createApiKeyFormId = React.useId(); + const segmentedControlId = `${createApiKeyFormId}-segmented-control`; const nameField = useFormControl('name', '', { type: 'text', @@ -34,7 +39,7 @@ export const CreateApiKeyForm = ({ onCreate }: CreateApiKeyFormProps) => { onCreate({ name: nameField.value, description: descriptionField.value || undefined, - expiration: undefined, + expiration, closeFn: close, }); }; @@ -49,11 +54,44 @@ export const CreateApiKeyForm = ({ onCreate }: CreateApiKeyFormProps) => { {showAdvanced && ( - - - + <> + + + + + + Expiration + + setExpiration(value as Expiration)} + fullWidth + > + + + + + + + )} - {/* TODO: Add Expiration column */} Date: Fri, 9 May 2025 11:01:04 -0700 Subject: [PATCH 022/116] chore: add api keys component or user and org profile --- packages/clerk-js/sandbox/template.html | 2 +- .../src/ui/components/ApiKeys/ApiKeys.tsx | 351 +++++------------- .../ui/components/ApiKeys/ApiKeysTable.tsx | 119 ++++++ .../src/ui/components/ApiKeys/shared.ts | 94 +++++ .../OrganizationApiKeysPage.tsx | 38 ++ .../OrganizationProfileRoutes.tsx | 16 +- .../ui/components/UserProfile/ApiKeysPage.tsx | 36 +- .../UserProfile/UserProfileRoutes.tsx | 2 +- packages/clerk-js/src/ui/constants.ts | 1 + .../components/OrganizationProfile.ts | 3 + 10 files changed, 405 insertions(+), 257 deletions(-) create mode 100644 packages/clerk-js/src/ui/components/ApiKeys/ApiKeysTable.tsx create mode 100644 packages/clerk-js/src/ui/components/ApiKeys/shared.ts create mode 100644 packages/clerk-js/src/ui/components/OrganizationProfile/OrganizationApiKeysPage.tsx diff --git a/packages/clerk-js/sandbox/template.html b/packages/clerk-js/sandbox/template.html index 4d13f32196a..b1f51b01ff5 100644 --- a/packages/clerk-js/sandbox/template.html +++ b/packages/clerk-js/sandbox/template.html @@ -263,7 +263,7 @@
  • Manage API Keys diff --git a/packages/clerk-js/src/ui/components/ApiKeys/ApiKeys.tsx b/packages/clerk-js/src/ui/components/ApiKeys/ApiKeys.tsx index 7b890c1f4e0..ab0b894acb6 100644 --- a/packages/clerk-js/src/ui/components/ApiKeys/ApiKeys.tsx +++ b/packages/clerk-js/src/ui/components/ApiKeys/ApiKeys.tsx @@ -1,31 +1,18 @@ -import { useClerk } from '@clerk/shared/react'; +import { useClerk, useOrganization, useUser } from '@clerk/shared/react'; import { useState } from 'react'; import { useApiKeysContext } from '../../contexts'; -import { - Box, - Button, - Col, - Flex, - Flow, - Icon, - Input, - Spinner, - Table, - Tbody, - Td, - Text, - Th, - Thead, - Tr, -} from '../../customizables'; -import { Card, InputWithIcon, Pagination, ThreeDotsMenu, withCardStateProvider } from '../../elements'; +import { Box, Button, Col, Flex, Flow, Icon } from '../../customizables'; +import { Card, InputWithIcon, Pagination, withCardStateProvider } from '../../elements'; import { Action } from '../../elements/Action'; -import { useClipboard, useFetch } from '../../hooks'; -import { Clipboard, Eye, EyeSlash, MagnifyingGlass } from '../../icons'; -import { CreateApiKeyForm, Expiration } from './CreateApiKeyForm'; - -const CopyButton = ({ apiKeyID }: { apiKeyID: string }) => { +import { useClipboard } from '../../hooks'; +import { Clipboard, MagnifyingGlass } from '../../icons'; +import { ApiKeysTable } from './ApiKeysTable'; +import type { Expiration } from './CreateApiKeyForm'; +import { CreateApiKeyForm } from './CreateApiKeyForm'; +import { useApiKeys } from './shared'; + +export const CopyButton = ({ apiKeyID }: { apiKeyID: string }) => { const clerk = useClerk(); const [text, setText] = useState(''); const { onCopy, hasCopied } = useClipboard(text); @@ -49,239 +36,103 @@ const CopyButton = ({ apiKeyID }: { apiKeyID: string }) => { ); }; -function getTimeLeftInSeconds(expirationOption: Expiration) { - if (expirationOption === 'never') { - return; - } - - const now = new Date(); - const future = new Date(now); - - if (expirationOption === '30d') { - future.setDate(future.getDate() + 30); - } else if (expirationOption === '90d') { - future.setDate(future.getDate() + 90); - } else { - throw new Error('TODO: Improve time handling'); - } - - const diffInMs = future.getTime() - now.getTime(); - const diffInSecs = Math.floor(diffInMs / 1000); - - return diffInSecs; -} +export const ApiKeysInternal = ({ + apiKeys, + isLoading, + revealedKeys, + toggleSecret, + revokeApiKey, + CopyButton, + search, + setSearch, + page, + setPage, + pageCount, + itemCount, + startingRow, + endingRow, + handleCreate, +}: { + apiKeys: any[]; + isLoading: boolean; + revealedKeys: Record; + toggleSecret: (id: string) => void; + revokeApiKey: (id: string) => void; + CopyButton: React.ComponentType<{ apiKeyID: string }>; + search: string; + setSearch: (s: string) => void; + page: number; + setPage: (n: number) => void; + pageCount: number; + itemCount: number; + startingRow: number; + endingRow: number; + handleCreate: (params: { name: string; description?: string; expiration: Expiration; closeFn: () => void }) => void; +}) => { + return ( + + + + + } + value={search} + onChange={e => { + setSearch(e.target.value); + setPage(1); + }} + /> + + + + + + + ({ paddingTop: t.space.$6, paddingBottom: t.space.$6 })}> + + void handleCreate(params)} /> + + + + + + {itemCount > 5 && ( + + )} + + ); +}; export const ApiKeys = withCardStateProvider(() => { - const clerk = useClerk(); const ctx = useApiKeysContext(); - const { - data: apiKeys, - isLoading, - revalidate, - } = useFetch(clerk.getApiKeys, { subject: ctx.subject }, undefined, `api-key-source-${ctx.subject}`); - const [revealedKeys, setRevealedKeys] = useState>({}); - const [page, setPage] = useState(1); - const [search, setSearch] = useState(''); - const itemsPerPage = ctx.perPage ?? 5; - - const filteredApiKeys = (apiKeys ?? []).filter(key => key.name.toLowerCase().includes(search.toLowerCase())); - - const itemCount = filteredApiKeys.length; - const pageCount = Math.max(1, Math.ceil(itemCount / itemsPerPage)); - const startingRow = itemCount > 0 ? (page - 1) * itemsPerPage + 1 : 0; - const endingRow = Math.min(page * itemsPerPage, itemCount); - const paginatedApiKeys = filteredApiKeys.slice(startingRow - 1, endingRow); - - const toggleSecret = async (id: string) => { - setRevealedKeys(prev => { - if (prev[id]) { - return { ...prev, [id]: null }; - } - return prev; - }); - - if (!revealedKeys[id]) { - const secret = await clerk.getApiKeySecret(id); - setRevealedKeys(prev => ({ ...prev, [id]: secret })); - } - }; - - const handleCreate = async (params: { - name: string; - description?: string; - expiration: Expiration; - closeFn: () => void; - }) => { - await clerk.createApiKey({ - name: params.name, - creationReason: params.description, - secondsUntilExpiration: getTimeLeftInSeconds(params.expiration), - }); - params.closeFn(); - revalidate(); - }; - - const revokeApiKey = async (apiKeyID: string) => { - await clerk.revokeApiKey({ apiKeyID, revocationReason: 'Revoked by user' }); - setPage(1); - revalidate(); - }; - + const { user } = useUser(); + const { organization } = useOrganization(); + const apiKeysManager = useApiKeys(ctx.subject ?? organization?.id ?? user?.id ?? '', ctx.perPage); return ( - - - - - } - value={search} - onChange={e => { - setSearch(e.target.value); - setPage(1); - }} - /> - - - - - - - - ({ - paddingTop: t.space.$6, - paddingBottom: t.space.$6, - })} - > - - void handleCreate(params)} /> - - - - - - - - - - - - - - - - {isLoading ? ( - - - - ) : ( - paginatedApiKeys.map(apiKey => ( - - - - - - - )) - )} - -
    NameLast usedKeyActions
    - -
    - {apiKey.name} - - Created at{' '} - {apiKey.createdAt.toLocaleDateString(undefined, { - month: 'short', - day: '2-digit', - year: 'numeric', - })} - - - - 3d ago - - - - - - - - - - - - void revokeApiKey(apiKey.id), - isDisabled: false, - }, - ]} - elementId={'member'} - /> -
    - - {itemCount > 5 && ( - - )} - +
    diff --git a/packages/clerk-js/src/ui/components/ApiKeys/ApiKeysTable.tsx b/packages/clerk-js/src/ui/components/ApiKeys/ApiKeysTable.tsx new file mode 100644 index 00000000000..52f814239da --- /dev/null +++ b/packages/clerk-js/src/ui/components/ApiKeys/ApiKeysTable.tsx @@ -0,0 +1,119 @@ +import React from 'react'; + +import { Button, Flex, Icon, Input, Spinner, Table, Tbody, Td, Text, Th, Thead, Tr } from '../../customizables'; +import { ThreeDotsMenu } from '../../elements'; +import { Eye, EyeSlash } from '../../icons'; + +export const ApiKeysTable = ({ + apiKeys, + isLoading, + revealedKeys, + toggleSecret, + revokeApiKey, + CopyButton, +}: { + apiKeys: any[]; + isLoading: boolean; + revealedKeys: Record; + toggleSecret: (id: string) => void; + revokeApiKey: (id: string) => void; + CopyButton: React.ComponentType<{ apiKeyID: string }>; +}) => { + return ( + + + + + + + + + + + {isLoading ? ( + + + + ) : ( + apiKeys.map(apiKey => ( + + + + + + + )) + )} + +
    NameLast usedKeyActions
    + +
    + {apiKey.name} + + Created at{' '} + {apiKey.createdAt.toLocaleDateString(undefined, { + month: 'short', + day: '2-digit', + year: 'numeric', + })} + + + + 3d ago + + + + + + + + + + + void revokeApiKey(apiKey.id), + isDisabled: false, + }, + ]} + elementId={'member'} + /> +
    + ); +}; diff --git a/packages/clerk-js/src/ui/components/ApiKeys/shared.ts b/packages/clerk-js/src/ui/components/ApiKeys/shared.ts new file mode 100644 index 00000000000..653d740de66 --- /dev/null +++ b/packages/clerk-js/src/ui/components/ApiKeys/shared.ts @@ -0,0 +1,94 @@ +import { useClerk } from '@clerk/shared/react'; +import { useState } from 'react'; + +import { useFetch } from '../../hooks'; +import type { Expiration } from './CreateApiKeyForm'; + +function getTimeLeftInSeconds(expirationOption: Expiration) { + if (expirationOption === 'never') { + return; + } + const now = new Date(); + const future = new Date(now); + if (expirationOption === '30d') { + future.setDate(future.getDate() + 30); + } else if (expirationOption === '90d') { + future.setDate(future.getDate() + 90); + } else { + throw new Error('TODO: Improve time handling'); + } + const diffInMs = future.getTime() - now.getTime(); + const diffInSecs = Math.floor(diffInMs / 1000); + return diffInSecs; +} + +export function useApiKeys(subject: string, perPage: number = 5) { + const clerk = useClerk(); + const { + data: apiKeys, + isLoading, + revalidate, + } = useFetch(clerk.getApiKeys, { subject }, undefined, `api-key-source-${subject}`); + const [revealedKeys, setRevealedKeys] = useState>({}); + const [page, setPage] = useState(1); + const [search, setSearch] = useState(''); + const itemsPerPage = perPage; + + const filteredApiKeys = (apiKeys ?? []).filter(key => key.name.toLowerCase().includes(search.toLowerCase())); + const itemCount = filteredApiKeys.length; + const pageCount = Math.max(1, Math.ceil(itemCount / itemsPerPage)); + const startingRow = itemCount > 0 ? (page - 1) * itemsPerPage + 1 : 0; + const endingRow = Math.min(page * itemsPerPage, itemCount); + const paginatedApiKeys = filteredApiKeys.slice(startingRow - 1, endingRow); + + const toggleSecret = async (id: string) => { + setRevealedKeys(prev => { + if (prev[id]) { + return { ...prev, [id]: null }; + } + return prev; + }); + if (!revealedKeys[id]) { + const secret = await clerk.getApiKeySecret(id); + setRevealedKeys(prev => ({ ...prev, [id]: secret })); + } + }; + + const handleCreate = async (params: { + name: string; + description?: string; + expiration: Expiration; + closeFn: () => void; + }) => { + await clerk.createApiKey({ + name: params.name, + creationReason: params.description, + secondsUntilExpiration: getTimeLeftInSeconds(params.expiration), + }); + params.closeFn(); + revalidate(); + }; + + const revokeApiKey = async (apiKeyID: string) => { + await clerk.revokeApiKey({ apiKeyID, revocationReason: 'Revoked by user' }); + setPage(1); + revalidate(); + }; + + return { + apiKeys: paginatedApiKeys, + isLoading: isLoading ?? false, + revealedKeys, + toggleSecret, + revokeApiKey, + search, + setSearch, + page, + setPage, + pageCount, + itemCount, + startingRow, + endingRow, + handleCreate, + }; +} diff --git a/packages/clerk-js/src/ui/components/OrganizationProfile/OrganizationApiKeysPage.tsx b/packages/clerk-js/src/ui/components/OrganizationProfile/OrganizationApiKeysPage.tsx new file mode 100644 index 00000000000..27bb37f0eda --- /dev/null +++ b/packages/clerk-js/src/ui/components/OrganizationProfile/OrganizationApiKeysPage.tsx @@ -0,0 +1,38 @@ +import { useOrganization } from '@clerk/shared/react'; + +import { ApiKeysContext, useApiKeysContext } from '../../contexts'; +import { Col } from '../../customizables'; +import { Header } from '../../elements'; +import { ApiKeysInternal, CopyButton } from '../ApiKeys'; +import { useApiKeys } from '../ApiKeys/shared'; + +function APIKeysPageInternal() { + const ctx = useApiKeysContext(); + const { organization } = useOrganization(); + const apiKeys = useApiKeys(organization?.id ?? '', ctx.perPage); + return ( + + + + ); +} + +export const OrganizationApiKeysPage = () => { + return ( + + + {/* TODO: Add localization key */} + + + + + + + ); +}; diff --git a/packages/clerk-js/src/ui/components/OrganizationProfile/OrganizationProfileRoutes.tsx b/packages/clerk-js/src/ui/components/OrganizationProfile/OrganizationProfileRoutes.tsx index 1b51625a650..59c0eca0536 100644 --- a/packages/clerk-js/src/ui/components/OrganizationProfile/OrganizationProfileRoutes.tsx +++ b/packages/clerk-js/src/ui/components/OrganizationProfile/OrganizationProfileRoutes.tsx @@ -15,8 +15,15 @@ const OrganizationBillingPage = lazy(() => })), ); +const OrganizationApiKeysPage = lazy(() => + import(/* webpackChunkName: "op-api-keys-page"*/ './OrganizationApiKeysPage').then(module => ({ + default: module.OrganizationApiKeysPage, + })), +); + export const OrganizationProfileRoutes = () => { - const { pages, isMembersPageRoot, isGeneralPageRoot, isBillingPageRoot } = useOrganizationProfileContext(); + const { pages, isMembersPageRoot, isGeneralPageRoot, isBillingPageRoot, isApiKeysPageRoot } = + useOrganizationProfileContext(); const { commerceSettings } = useEnvironment(); const customPageRoutesWithContents = pages.contents?.map((customPage, index) => { @@ -60,6 +67,13 @@ export const OrganizationProfileRoutes = () => { + + + + + + + {commerceSettings.billing.enabled && commerceSettings.billing.hasPaidOrgPlans && ( diff --git a/packages/clerk-js/src/ui/components/UserProfile/ApiKeysPage.tsx b/packages/clerk-js/src/ui/components/UserProfile/ApiKeysPage.tsx index 351f0316505..9e5ead04912 100644 --- a/packages/clerk-js/src/ui/components/UserProfile/ApiKeysPage.tsx +++ b/packages/clerk-js/src/ui/components/UserProfile/ApiKeysPage.tsx @@ -1,10 +1,38 @@ -import { ApiKeysContext } from '../../contexts'; -import { ApiKeys } from '../ApiKeys'; +import { useUser } from '@clerk/shared/react'; -export const ApiKeysPage = () => { +import { ApiKeysContext, useApiKeysContext } from '../../contexts'; +import { Col } from '../../customizables'; +import { Header } from '../../elements'; +import { ApiKeysInternal, CopyButton } from '../ApiKeys'; +import { useApiKeys } from '../ApiKeys/shared'; + +function APIKeysPageInternal() { + const ctx = useApiKeysContext(); + const { user } = useUser(); + const apiKeys = useApiKeys(user?.id ?? '', ctx.perPage); return ( - + ); +} + +export const ApiKeysPage = () => { + return ( + + + + + + + + + ); }; diff --git a/packages/clerk-js/src/ui/components/UserProfile/UserProfileRoutes.tsx b/packages/clerk-js/src/ui/components/UserProfile/UserProfileRoutes.tsx index 6017c520938..d921685f52e 100644 --- a/packages/clerk-js/src/ui/components/UserProfile/UserProfileRoutes.tsx +++ b/packages/clerk-js/src/ui/components/UserProfile/UserProfileRoutes.tsx @@ -16,7 +16,7 @@ const BillingPage = lazy(() => ); const ApiKeysPage = lazy(() => - import(/* webpackChunkName: "up-api-key-page"*/ './ApiKeysPage').then(module => ({ + import(/* webpackChunkName: "up-api-keys-page"*/ './ApiKeysPage').then(module => ({ default: module.ApiKeysPage, })), ); diff --git a/packages/clerk-js/src/ui/constants.ts b/packages/clerk-js/src/ui/constants.ts index 5be19ae5358..b32e4bc8b7f 100644 --- a/packages/clerk-js/src/ui/constants.ts +++ b/packages/clerk-js/src/ui/constants.ts @@ -9,6 +9,7 @@ export const ORGANIZATION_PROFILE_NAVBAR_ROUTE_ID = { GENERAL: 'general', MEMBERS: 'members', BILLING: 'billing', + API_KEYS: 'apiKeys', }; export const USER_BUTTON_ITEM_ID = { diff --git a/packages/clerk-js/src/ui/contexts/components/OrganizationProfile.ts b/packages/clerk-js/src/ui/contexts/components/OrganizationProfile.ts index 2ec5ccffd0a..9af4850a8ff 100644 --- a/packages/clerk-js/src/ui/contexts/components/OrganizationProfile.ts +++ b/packages/clerk-js/src/ui/contexts/components/OrganizationProfile.ts @@ -23,6 +23,7 @@ export type OrganizationProfileContextType = OrganizationProfileCtx & { isMembersPageRoot: boolean; isGeneralPageRoot: boolean; isBillingPageRoot: boolean; + isApiKeysPageRoot: boolean; }; export const OrganizationProfileContext = createContext(null); @@ -50,6 +51,7 @@ export const useOrganizationProfileContext = (): OrganizationProfileContextType const isMembersPageRoot = pages.routes[0].id === ORGANIZATION_PROFILE_NAVBAR_ROUTE_ID.MEMBERS; const isGeneralPageRoot = pages.routes[0].id === ORGANIZATION_PROFILE_NAVBAR_ROUTE_ID.GENERAL; const isBillingPageRoot = pages.routes[0].id === ORGANIZATION_PROFILE_NAVBAR_ROUTE_ID.BILLING; + const isApiKeysPageRoot = pages.routes[0].id === ORGANIZATION_PROFILE_NAVBAR_ROUTE_ID.API_KEYS; const navigateToGeneralPageRoot = () => navigate(isGeneralPageRoot ? '../' : isMembersPageRoot ? './organization-general' : '../organization-general'); @@ -62,5 +64,6 @@ export const useOrganizationProfileContext = (): OrganizationProfileContextType isMembersPageRoot, isGeneralPageRoot, isBillingPageRoot, + isApiKeysPageRoot, }; }; From f19825caa945a7479883deeec0b9af6d58cdc1cd Mon Sep 17 00:00:00 2001 From: wobsoriano Date: Fri, 9 May 2025 11:42:11 -0700 Subject: [PATCH 023/116] chore: add missing org profile sidebar nav --- .../OrganizationProfileRoutes.tsx | 16 +++++++++------- .../components/UserProfile/UserProfileRoutes.tsx | 4 +++- .../clerk-js/src/ui/utils/createCustomPages.tsx | 6 ++++++ 3 files changed, 18 insertions(+), 8 deletions(-) diff --git a/packages/clerk-js/src/ui/components/OrganizationProfile/OrganizationProfileRoutes.tsx b/packages/clerk-js/src/ui/components/OrganizationProfile/OrganizationProfileRoutes.tsx index 59c0eca0536..0aac970db95 100644 --- a/packages/clerk-js/src/ui/components/OrganizationProfile/OrganizationProfileRoutes.tsx +++ b/packages/clerk-js/src/ui/components/OrganizationProfile/OrganizationProfileRoutes.tsx @@ -67,13 +67,6 @@ export const OrganizationProfileRoutes = () => { - - - - - - - {commerceSettings.billing.enabled && commerceSettings.billing.hasPaidOrgPlans && ( @@ -103,6 +96,15 @@ export const OrganizationProfileRoutes = () => { )} + + + + + + + + + ); diff --git a/packages/clerk-js/src/ui/components/UserProfile/UserProfileRoutes.tsx b/packages/clerk-js/src/ui/components/UserProfile/UserProfileRoutes.tsx index d921685f52e..b16211be26d 100644 --- a/packages/clerk-js/src/ui/components/UserProfile/UserProfileRoutes.tsx +++ b/packages/clerk-js/src/ui/components/UserProfile/UserProfileRoutes.tsx @@ -90,7 +90,9 @@ export const UserProfileRoutes = () => { - + + + diff --git a/packages/clerk-js/src/ui/utils/createCustomPages.tsx b/packages/clerk-js/src/ui/utils/createCustomPages.tsx index 87b683ab0b2..32e6eac3776 100644 --- a/packages/clerk-js/src/ui/utils/createCustomPages.tsx +++ b/packages/clerk-js/src/ui/utils/createCustomPages.tsx @@ -305,6 +305,12 @@ const getOrganizationProfileDefaultRoutes = ({ commerce }: { commerce: boolean } icon: Users, path: 'organization-members', }, + { + name: 'API keys', + id: ORGANIZATION_PROFILE_NAVBAR_ROUTE_ID.API_KEYS, + icon: InformationCircle, + path: 'organization-api-keys', + }, ]; if (commerce) { INITIAL_ROUTES.push({ From fb78ddc7049538991d0d13a50484febe6ffb4f99 Mon Sep 17 00:00:00 2001 From: wobsoriano Date: Fri, 9 May 2025 13:00:49 -0700 Subject: [PATCH 024/116] chore: clean up props --- .../src/ui/components/ApiKeys/ApiKeys.tsx | 92 +++++-------------- .../ui/components/ApiKeys/ApiKeysTable.tsx | 42 +++++---- .../src/ui/components/ApiKeys/shared.ts | 18 +--- .../OrganizationApiKeysPage.tsx | 23 +---- .../ui/components/UserProfile/ApiKeysPage.tsx | 21 +---- 5 files changed, 53 insertions(+), 143 deletions(-) diff --git a/packages/clerk-js/src/ui/components/ApiKeys/ApiKeys.tsx b/packages/clerk-js/src/ui/components/ApiKeys/ApiKeys.tsx index ab0b894acb6..a28ced232fd 100644 --- a/packages/clerk-js/src/ui/components/ApiKeys/ApiKeys.tsx +++ b/packages/clerk-js/src/ui/components/ApiKeys/ApiKeys.tsx @@ -1,74 +1,30 @@ -import { useClerk, useOrganization, useUser } from '@clerk/shared/react'; -import { useState } from 'react'; +import { useOrganization, useUser } from '@clerk/shared/react'; import { useApiKeysContext } from '../../contexts'; import { Box, Button, Col, Flex, Flow, Icon } from '../../customizables'; import { Card, InputWithIcon, Pagination, withCardStateProvider } from '../../elements'; import { Action } from '../../elements/Action'; -import { useClipboard } from '../../hooks'; -import { Clipboard, MagnifyingGlass } from '../../icons'; +import { MagnifyingGlass } from '../../icons'; import { ApiKeysTable } from './ApiKeysTable'; -import type { Expiration } from './CreateApiKeyForm'; import { CreateApiKeyForm } from './CreateApiKeyForm'; import { useApiKeys } from './shared'; -export const CopyButton = ({ apiKeyID }: { apiKeyID: string }) => { - const clerk = useClerk(); - const [text, setText] = useState(''); - const { onCopy, hasCopied } = useClipboard(text); +export const ApiKeysInternal = ({ subject }: { subject: string }) => { + const { + apiKeys, + isLoading, + revokeApiKey, + search, + setSearch, + page, + setPage, + pageCount, + itemCount, + startingRow, + endingRow, + handleCreate, + } = useApiKeys({ subject }); - const fetchSecret = async () => { - const secret = await clerk.getApiKeySecret(apiKeyID); - setText(secret); - onCopy(); - }; - - return ( - - ); -}; - -export const ApiKeysInternal = ({ - apiKeys, - isLoading, - revealedKeys, - toggleSecret, - revokeApiKey, - CopyButton, - search, - setSearch, - page, - setPage, - pageCount, - itemCount, - startingRow, - endingRow, - handleCreate, -}: { - apiKeys: any[]; - isLoading: boolean; - revealedKeys: Record; - toggleSecret: (id: string) => void; - revokeApiKey: (id: string) => void; - CopyButton: React.ComponentType<{ apiKeyID: string }>; - search: string; - setSearch: (s: string) => void; - page: number; - setPage: (n: number) => void; - pageCount: number; - itemCount: number; - startingRow: number; - endingRow: number; - handleCreate: (params: { name: string; description?: string; expiration: Expiration; closeFn: () => void }) => void; -}) => { return ( @@ -100,12 +56,9 @@ export const ApiKeysInternal = ({ {itemCount > 5 && ( { const ctx = useApiKeysContext(); const { user } = useUser(); const { organization } = useOrganization(); - const apiKeysManager = useApiKeys(ctx.subject ?? organization?.id ?? user?.id ?? '', ctx.perPage); + return ( - + diff --git a/packages/clerk-js/src/ui/components/ApiKeys/ApiKeysTable.tsx b/packages/clerk-js/src/ui/components/ApiKeys/ApiKeysTable.tsx index 52f814239da..6fbcdfc8f7d 100644 --- a/packages/clerk-js/src/ui/components/ApiKeys/ApiKeysTable.tsx +++ b/packages/clerk-js/src/ui/components/ApiKeys/ApiKeysTable.tsx @@ -1,23 +1,17 @@ -import React from 'react'; +import type { ApiKeyResource } from '@clerk/types'; import { Button, Flex, Icon, Input, Spinner, Table, Tbody, Td, Text, Th, Thead, Tr } from '../../customizables'; import { ThreeDotsMenu } from '../../elements'; -import { Eye, EyeSlash } from '../../icons'; +import { Clipboard, Eye } from '../../icons'; export const ApiKeysTable = ({ - apiKeys, + rows, isLoading, - revealedKeys, - toggleSecret, - revokeApiKey, - CopyButton, + onRevoke, }: { - apiKeys: any[]; + rows: ApiKeyResource[]; isLoading: boolean; - revealedKeys: Record; - toggleSecret: (id: string) => void; - revokeApiKey: (id: string) => void; - CopyButton: React.ComponentType<{ apiKeyID: string }>; + onRevoke: (id: string) => void; }) => { return ( @@ -40,7 +34,7 @@ export const ApiKeysTable = ({ ) : ( - apiKeys.map(apiKey => ( + rows.map(apiKey => ( - {/* TODO: Add localization key */} - + ); diff --git a/packages/clerk-js/src/ui/components/UserProfile/ApiKeysPage.tsx b/packages/clerk-js/src/ui/components/UserProfile/ApiKeysPage.tsx index 9e5ead04912..9c546430613 100644 --- a/packages/clerk-js/src/ui/components/UserProfile/ApiKeysPage.tsx +++ b/packages/clerk-js/src/ui/components/UserProfile/ApiKeysPage.tsx @@ -1,26 +1,13 @@ import { useUser } from '@clerk/shared/react'; -import { ApiKeysContext, useApiKeysContext } from '../../contexts'; +import { ApiKeysContext } from '../../contexts'; import { Col } from '../../customizables'; import { Header } from '../../elements'; -import { ApiKeysInternal, CopyButton } from '../ApiKeys'; -import { useApiKeys } from '../ApiKeys/shared'; +import { ApiKeysInternal } from '../ApiKeys'; -function APIKeysPageInternal() { - const ctx = useApiKeysContext(); +export const ApiKeysPage = () => { const { user } = useUser(); - const apiKeys = useApiKeys(user?.id ?? '', ctx.perPage); - return ( - - - - ); -} -export const ApiKeysPage = () => { return ( @@ -31,7 +18,7 @@ export const ApiKeysPage = () => { /> - + ); From 0b2712c62b3b97edd0fadecab0d1e0af1a6cbfc1 Mon Sep 17 00:00:00 2001 From: wobsoriano Date: Fri, 9 May 2025 14:42:09 -0700 Subject: [PATCH 025/116] chore: clean up props --- .../ui/components/ApiKeys/ApiKeysTable.tsx | 26 ++++++++++++------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/packages/clerk-js/src/ui/components/ApiKeys/ApiKeysTable.tsx b/packages/clerk-js/src/ui/components/ApiKeys/ApiKeysTable.tsx index 6fbcdfc8f7d..e47cdc81e9f 100644 --- a/packages/clerk-js/src/ui/components/ApiKeys/ApiKeysTable.tsx +++ b/packages/clerk-js/src/ui/components/ApiKeys/ApiKeysTable.tsx @@ -2,8 +2,24 @@ import type { ApiKeyResource } from '@clerk/types'; import { Button, Flex, Icon, Input, Spinner, Table, Tbody, Td, Text, Th, Thead, Tr } from '../../customizables'; import { ThreeDotsMenu } from '../../elements'; +import { useClipboard } from '../../hooks'; import { Clipboard, Eye } from '../../icons'; +const CopyButton = ({ text }: { text: string }) => { + const { onCopy } = useClipboard(text); + return ( + + ); +}; + export const ApiKeysTable = ({ rows, isLoading, @@ -87,15 +103,7 @@ export const ApiKeysTable = ({ - {/* */} - + - + - + @@ -93,10 +93,7 @@ export const ApiKeysTable = ({ + + + ); +}; diff --git a/packages/clerk-js/src/ui/components/ApiKeys/CreateApiKeyForm.tsx b/packages/clerk-js/src/ui/components/ApiKeys/CreateApiKeyForm.tsx index 91d9cf1f508..c8f172b26d5 100644 --- a/packages/clerk-js/src/ui/components/ApiKeys/CreateApiKeyForm.tsx +++ b/packages/clerk-js/src/ui/components/ApiKeys/CreateApiKeyForm.tsx @@ -35,7 +35,7 @@ export const CreateApiKeyForm = ({ onCreate, isSubmitting }: CreateApiKeyFormPro isRequired: false, }); - const canSubmit = nameField.value.length > 1; + const canSubmit = nameField.value.length > 2; const handleSubmit = (e: React.FormEvent) => { e.preventDefault(); @@ -51,8 +51,8 @@ export const CreateApiKeyForm = ({ onCreate, isSubmitting }: CreateApiKeyFormPro return ( diff --git a/packages/localizations/src/ar-SA.ts b/packages/localizations/src/ar-SA.ts index 7d165a21472..0f831c04434 100644 --- a/packages/localizations/src/ar-SA.ts +++ b/packages/localizations/src/ar-SA.ts @@ -14,9 +14,10 @@ import type { LocalizationResource } from '@clerk/types'; export const arSA: LocalizationResource = { locale: 'ar-SA', - apiKey: { + apiKeys: { action__add: undefined, action__search: undefined, + detailsTitle__emptyRow: undefined, formHint: undefined, formTitle: undefined, menuAction__revoke: undefined, @@ -204,6 +205,9 @@ export const arSA: LocalizationResource = { titleWithoutPersonal: 'أختر منظمة', }, organizationProfile: { + apiKeysPage: { + title: undefined, + }, badge__automaticInvitation: 'دعوات تلقائية', badge__automaticSuggestion: 'أقتراحات تلقائية', badge__manualInvitation: 'ليس هناك تسجيل تلقائي', @@ -814,6 +818,9 @@ export const arSA: LocalizationResource = { action__signOutAll: 'تسجيل الخروج من جميع الحسابات', }, userProfile: { + apiKeysPage: { + title: undefined, + }, backupCodePage: { actionLabel__copied: 'تم النسخ', actionLabel__copy: 'نسخ الكل', diff --git a/packages/localizations/src/be-BY.ts b/packages/localizations/src/be-BY.ts index 6202a732fc3..0b7b9983902 100644 --- a/packages/localizations/src/be-BY.ts +++ b/packages/localizations/src/be-BY.ts @@ -14,9 +14,10 @@ import type { LocalizationResource } from '@clerk/types'; export const beBY: LocalizationResource = { locale: 'be-BY', - apiKey: { + apiKeys: { action__add: undefined, action__search: undefined, + detailsTitle__emptyRow: undefined, formHint: undefined, formTitle: undefined, menuAction__revoke: undefined, @@ -205,6 +206,9 @@ export const beBY: LocalizationResource = { titleWithoutPersonal: 'Выберыце арганізацыю', }, organizationProfile: { + apiKeysPage: { + title: undefined, + }, badge__automaticInvitation: 'Automatic invitations', badge__automaticSuggestion: 'Automatic suggestions', badge__manualInvitation: 'No automatic enrollment', @@ -822,6 +826,9 @@ export const beBY: LocalizationResource = { action__signOutAll: 'Выйсці з усіх уліковых запісаў', }, userProfile: { + apiKeysPage: { + title: undefined, + }, backupCodePage: { actionLabel__copied: 'Скапіравана!', actionLabel__copy: 'Скапіраваць усё', diff --git a/packages/localizations/src/bg-BG.ts b/packages/localizations/src/bg-BG.ts index 20b07d51508..9be4a1bf142 100644 --- a/packages/localizations/src/bg-BG.ts +++ b/packages/localizations/src/bg-BG.ts @@ -14,9 +14,10 @@ import type { LocalizationResource } from '@clerk/types'; export const bgBG: LocalizationResource = { locale: 'bg-BG', - apiKey: { + apiKeys: { action__add: undefined, action__search: undefined, + detailsTitle__emptyRow: undefined, formHint: undefined, formTitle: undefined, menuAction__revoke: undefined, @@ -203,6 +204,9 @@ export const bgBG: LocalizationResource = { titleWithoutPersonal: 'Изберете организация', }, organizationProfile: { + apiKeysPage: { + title: undefined, + }, badge__automaticInvitation: 'Автоматични покани', badge__automaticSuggestion: 'Автоматични предложения', badge__manualInvitation: 'Няма автоматично включване', @@ -813,6 +817,9 @@ export const bgBG: LocalizationResource = { action__signOutAll: 'Изход от всички акаунти', }, userProfile: { + apiKeysPage: { + title: undefined, + }, backupCodePage: { actionLabel__copied: 'Копирано!', actionLabel__copy: 'Копиране на всички', diff --git a/packages/localizations/src/ca-ES.ts b/packages/localizations/src/ca-ES.ts index ae0e59d72c4..bcc3a7187cd 100644 --- a/packages/localizations/src/ca-ES.ts +++ b/packages/localizations/src/ca-ES.ts @@ -14,9 +14,10 @@ import type { LocalizationResource } from '@clerk/types'; export const caES: LocalizationResource = { locale: 'ca-ES', - apiKey: { + apiKeys: { action__add: undefined, action__search: undefined, + detailsTitle__emptyRow: undefined, formHint: undefined, formTitle: undefined, menuAction__revoke: undefined, @@ -203,6 +204,9 @@ export const caES: LocalizationResource = { titleWithoutPersonal: 'Trieu una organització', }, organizationProfile: { + apiKeysPage: { + title: undefined, + }, badge__automaticInvitation: 'Invitacions automàtiques', badge__automaticSuggestion: 'Suggeriments automàtics', badge__manualInvitation: 'Sense inscripció automàtica', @@ -817,6 +821,9 @@ export const caES: LocalizationResource = { action__signOutAll: 'Tanca sessió de tots els comptes', }, userProfile: { + apiKeysPage: { + title: undefined, + }, backupCodePage: { actionLabel__copied: 'Copiat!', actionLabel__copy: 'Copia tot', diff --git a/packages/localizations/src/cs-CZ.ts b/packages/localizations/src/cs-CZ.ts index a73814590fd..d6503fa9d1f 100644 --- a/packages/localizations/src/cs-CZ.ts +++ b/packages/localizations/src/cs-CZ.ts @@ -14,9 +14,10 @@ import type { LocalizationResource } from '@clerk/types'; export const csCZ: LocalizationResource = { locale: 'cs-CZ', - apiKey: { + apiKeys: { action__add: undefined, action__search: undefined, + detailsTitle__emptyRow: undefined, formHint: undefined, formTitle: undefined, menuAction__revoke: undefined, @@ -205,6 +206,9 @@ export const csCZ: LocalizationResource = { titleWithoutPersonal: 'Choose an organization', }, organizationProfile: { + apiKeysPage: { + title: undefined, + }, badge__automaticInvitation: 'Automatic invitations', badge__automaticSuggestion: 'Automatic suggestions', badge__manualInvitation: 'No automatic enrollment', @@ -813,6 +817,9 @@ export const csCZ: LocalizationResource = { action__signOutAll: 'Odhlásit se ze všech účtů', }, userProfile: { + apiKeysPage: { + title: undefined, + }, backupCodePage: { actionLabel__copied: 'Zkopírováno!', actionLabel__copy: 'Zkopírovat vše', diff --git a/packages/localizations/src/da-DK.ts b/packages/localizations/src/da-DK.ts index 6abca6e4de2..4b8d15f7499 100644 --- a/packages/localizations/src/da-DK.ts +++ b/packages/localizations/src/da-DK.ts @@ -14,9 +14,10 @@ import type { LocalizationResource } from '@clerk/types'; export const daDK: LocalizationResource = { locale: 'da-DK', - apiKey: { + apiKeys: { action__add: undefined, action__search: undefined, + detailsTitle__emptyRow: undefined, formHint: undefined, formTitle: undefined, menuAction__revoke: undefined, @@ -204,6 +205,9 @@ export const daDK: LocalizationResource = { titleWithoutPersonal: 'Vælg en organisation', }, organizationProfile: { + apiKeysPage: { + title: undefined, + }, badge__automaticInvitation: 'Automatiske invitationer', badge__automaticSuggestion: 'Automatiske forslag', badge__manualInvitation: 'Ingen automatisk tilmelding', @@ -814,6 +818,9 @@ export const daDK: LocalizationResource = { action__signOutAll: 'Log ud af alle konti', }, userProfile: { + apiKeysPage: { + title: undefined, + }, backupCodePage: { actionLabel__copied: 'Kopieret!', actionLabel__copy: 'Kopier alle', diff --git a/packages/localizations/src/de-DE.ts b/packages/localizations/src/de-DE.ts index 523b482e8c3..5bc40a1120b 100644 --- a/packages/localizations/src/de-DE.ts +++ b/packages/localizations/src/de-DE.ts @@ -14,9 +14,10 @@ import type { LocalizationResource } from '@clerk/types'; export const deDE: LocalizationResource = { locale: 'de-DE', - apiKey: { + apiKeys: { action__add: undefined, action__search: undefined, + detailsTitle__emptyRow: undefined, formHint: undefined, formTitle: undefined, menuAction__revoke: undefined, @@ -209,6 +210,9 @@ export const deDE: LocalizationResource = { titleWithoutPersonal: 'Organisation auswählen', }, organizationProfile: { + apiKeysPage: { + title: undefined, + }, badge__automaticInvitation: 'Automatische Einladungen', badge__automaticSuggestion: 'Automatische Vorschläge', badge__manualInvitation: 'Keine automatische Aufnahme', @@ -837,6 +841,9 @@ export const deDE: LocalizationResource = { action__signOutAll: 'Melden Sie sich von allen Konten ab', }, userProfile: { + apiKeysPage: { + title: undefined, + }, backupCodePage: { actionLabel__copied: 'Kopiert!', actionLabel__copy: 'Kopiere alle', diff --git a/packages/localizations/src/el-GR.ts b/packages/localizations/src/el-GR.ts index 79349d5aa02..fceb7d12424 100644 --- a/packages/localizations/src/el-GR.ts +++ b/packages/localizations/src/el-GR.ts @@ -14,9 +14,10 @@ import type { LocalizationResource } from '@clerk/types'; export const elGR: LocalizationResource = { locale: 'el-GR', - apiKey: { + apiKeys: { action__add: undefined, action__search: undefined, + detailsTitle__emptyRow: undefined, formHint: undefined, formTitle: undefined, menuAction__revoke: undefined, @@ -204,6 +205,9 @@ export const elGR: LocalizationResource = { titleWithoutPersonal: 'Επιλέξτε οργανισμό', }, organizationProfile: { + apiKeysPage: { + title: undefined, + }, badge__automaticInvitation: 'Αυτόματες προσκλήσεις', badge__automaticSuggestion: 'Αυτόματες προτάσεις', badge__manualInvitation: 'Χωρίς αυτόματη εγγραφή', @@ -822,6 +826,9 @@ export const elGR: LocalizationResource = { action__signOutAll: 'Αποσύνδεση από όλους τους λογαριασμούς', }, userProfile: { + apiKeysPage: { + title: undefined, + }, backupCodePage: { actionLabel__copied: 'Αντιγράφηκαν!', actionLabel__copy: 'Αντιγραφή όλων', diff --git a/packages/localizations/src/en-GB.ts b/packages/localizations/src/en-GB.ts index b63d446d5d2..9896cf10f06 100644 --- a/packages/localizations/src/en-GB.ts +++ b/packages/localizations/src/en-GB.ts @@ -14,9 +14,10 @@ import type { LocalizationResource } from '@clerk/types'; export const enGB: LocalizationResource = { locale: 'en-GB', - apiKey: { + apiKeys: { action__add: undefined, action__search: undefined, + detailsTitle__emptyRow: undefined, formHint: undefined, formTitle: undefined, menuAction__revoke: undefined, @@ -204,6 +205,9 @@ export const enGB: LocalizationResource = { titleWithoutPersonal: 'Choose an organisation', }, organizationProfile: { + apiKeysPage: { + title: undefined, + }, badge__automaticInvitation: 'Automatic invitations', badge__automaticSuggestion: 'Automatic suggestions', badge__manualInvitation: 'No automatic enrollment', @@ -822,6 +826,9 @@ export const enGB: LocalizationResource = { action__signOutAll: 'Sign out of all accounts', }, userProfile: { + apiKeysPage: { + title: undefined, + }, backupCodePage: { actionLabel__copied: 'Copied!', actionLabel__copy: 'Copy all', diff --git a/packages/localizations/src/en-US.ts b/packages/localizations/src/en-US.ts index f8c44fd7772..f7c20b946bc 100644 --- a/packages/localizations/src/en-US.ts +++ b/packages/localizations/src/en-US.ts @@ -2,9 +2,10 @@ import type { LocalizationResource } from '@clerk/types'; export const enUS: LocalizationResource = { locale: 'en-US', - apiKey: { + apiKeys: { action__add: 'Add new key', action__search: 'Search keys', + detailsTitle__emptyRow: 'No API keys found', formHint: 'Provide a name to generate a new key. You’ll be able to revoke it anytime.', formTitle: 'Add new API key', menuAction__revoke: 'Revoke key', @@ -195,6 +196,9 @@ export const enUS: LocalizationResource = { titleWithoutPersonal: 'Choose an organization', }, organizationProfile: { + apiKeysPage: { + title: 'API Keys', + }, badge__automaticInvitation: 'Automatic invitations', badge__automaticSuggestion: 'Automatic suggestions', badge__manualInvitation: 'No automatic enrollment', @@ -292,9 +296,6 @@ export const enUS: LocalizationResource = { headerTitle__requests: 'Requests', }, }, - apiKeysPage: { - title: 'API Keys', - }, navbar: { billing: 'Billing', description: 'Manage your organization.', @@ -813,6 +814,9 @@ export const enUS: LocalizationResource = { action__signOutAll: 'Sign out of all accounts', }, userProfile: { + apiKeysPage: { + title: 'API Keys', + }, backupCodePage: { actionLabel__copied: 'Copied!', actionLabel__copy: 'Copy all', @@ -916,9 +920,6 @@ export const enUS: LocalizationResource = { title: 'Add email address', verifyTitle: 'Verify email address', }, - apiKeysPage: { - title: 'API Keys', - }, formButtonPrimary__add: 'Add', formButtonPrimary__continue: 'Continue', formButtonPrimary__finish: 'Finish', diff --git a/packages/localizations/src/es-ES.ts b/packages/localizations/src/es-ES.ts index 4011e1e4815..ed4f67da9ca 100644 --- a/packages/localizations/src/es-ES.ts +++ b/packages/localizations/src/es-ES.ts @@ -14,9 +14,10 @@ import type { LocalizationResource } from '@clerk/types'; export const esES: LocalizationResource = { locale: 'es-ES', - apiKey: { + apiKeys: { action__add: undefined, action__search: undefined, + detailsTitle__emptyRow: undefined, formHint: undefined, formTitle: undefined, menuAction__revoke: undefined, @@ -204,6 +205,9 @@ export const esES: LocalizationResource = { titleWithoutPersonal: 'Escoja una organización', }, organizationProfile: { + apiKeysPage: { + title: undefined, + }, badge__automaticInvitation: 'Invitaciones automáticas', badge__automaticSuggestion: 'Sugerencias automáticas', badge__manualInvitation: 'Sin inscripción automática', @@ -819,6 +823,9 @@ export const esES: LocalizationResource = { action__signOutAll: 'Salir de todas las cuentas', }, userProfile: { + apiKeysPage: { + title: undefined, + }, backupCodePage: { actionLabel__copied: '¡Copiado!', actionLabel__copy: 'Copiar todo', diff --git a/packages/localizations/src/es-MX.ts b/packages/localizations/src/es-MX.ts index 744749cd844..9fd88178932 100644 --- a/packages/localizations/src/es-MX.ts +++ b/packages/localizations/src/es-MX.ts @@ -14,9 +14,10 @@ import type { LocalizationResource } from '@clerk/types'; export const esMX: LocalizationResource = { locale: 'es-MX', - apiKey: { + apiKeys: { action__add: undefined, action__search: undefined, + detailsTitle__emptyRow: undefined, formHint: undefined, formTitle: undefined, menuAction__revoke: undefined, @@ -205,6 +206,9 @@ export const esMX: LocalizationResource = { titleWithoutPersonal: 'Elige una organización', }, organizationProfile: { + apiKeysPage: { + title: undefined, + }, badge__automaticInvitation: 'Invitaciones automaticas', badge__automaticSuggestion: 'Sugerencias automaticas', badge__manualInvitation: 'Sin inscripciónes automaticas', @@ -820,6 +824,9 @@ export const esMX: LocalizationResource = { action__signOutAll: 'Salir de todas las cuentas', }, userProfile: { + apiKeysPage: { + title: undefined, + }, backupCodePage: { actionLabel__copied: 'Copiado!', actionLabel__copy: 'Copiar todo', diff --git a/packages/localizations/src/fi-FI.ts b/packages/localizations/src/fi-FI.ts index efdf03eecc1..01ce69d0fc7 100644 --- a/packages/localizations/src/fi-FI.ts +++ b/packages/localizations/src/fi-FI.ts @@ -14,9 +14,10 @@ import type { LocalizationResource } from '@clerk/types'; export const fiFI: LocalizationResource = { locale: 'fi-FI', - apiKey: { + apiKeys: { action__add: undefined, action__search: undefined, + detailsTitle__emptyRow: undefined, formHint: undefined, formTitle: undefined, menuAction__revoke: undefined, @@ -204,6 +205,9 @@ export const fiFI: LocalizationResource = { titleWithoutPersonal: 'Valitse organisaatio', }, organizationProfile: { + apiKeysPage: { + title: undefined, + }, badge__automaticInvitation: 'Automaattiset kutsut', badge__automaticSuggestion: 'Automaattiset ehdotukset', badge__manualInvitation: 'Ei automaattista liittymistä', @@ -817,6 +821,9 @@ export const fiFI: LocalizationResource = { action__signOutAll: 'Kirjaudu ulos kaikista tileistä', }, userProfile: { + apiKeysPage: { + title: undefined, + }, backupCodePage: { actionLabel__copied: 'Kopioitu', actionLabel__copy: 'Kopioi', diff --git a/packages/localizations/src/fr-FR.ts b/packages/localizations/src/fr-FR.ts index 1127a1e5b36..ab753ce9eef 100644 --- a/packages/localizations/src/fr-FR.ts +++ b/packages/localizations/src/fr-FR.ts @@ -14,9 +14,10 @@ import type { LocalizationResource } from '@clerk/types'; export const frFR: LocalizationResource = { locale: 'fr-FR', - apiKey: { + apiKeys: { action__add: undefined, action__search: undefined, + detailsTitle__emptyRow: undefined, formHint: undefined, formTitle: undefined, menuAction__revoke: undefined, @@ -205,6 +206,9 @@ export const frFR: LocalizationResource = { titleWithoutPersonal: 'Choisissez une organisation', }, organizationProfile: { + apiKeysPage: { + title: undefined, + }, badge__automaticInvitation: 'Invitations automatiques', badge__automaticSuggestion: 'Suggestions automatiques', badge__manualInvitation: "Pas d'inscription automatique", @@ -822,6 +826,9 @@ export const frFR: LocalizationResource = { action__signOutAll: 'Se déconnecter de tous les comptes', }, userProfile: { + apiKeysPage: { + title: undefined, + }, backupCodePage: { actionLabel__copied: 'Copié !', actionLabel__copy: 'Copier tous les codes', diff --git a/packages/localizations/src/he-IL.ts b/packages/localizations/src/he-IL.ts index 314bc2a0df9..7cb3ff620bb 100644 --- a/packages/localizations/src/he-IL.ts +++ b/packages/localizations/src/he-IL.ts @@ -14,9 +14,10 @@ import type { LocalizationResource } from '@clerk/types'; export const heIL: LocalizationResource = { locale: 'he-IL', - apiKey: { + apiKeys: { action__add: undefined, action__search: undefined, + detailsTitle__emptyRow: undefined, formHint: undefined, formTitle: undefined, menuAction__revoke: undefined, @@ -203,6 +204,9 @@ export const heIL: LocalizationResource = { titleWithoutPersonal: 'בחר ארגון', }, organizationProfile: { + apiKeysPage: { + title: undefined, + }, badge__automaticInvitation: 'הזמנות אוטומטיות', badge__automaticSuggestion: 'הצעות אוטומטיות', badge__manualInvitation: 'ללא הרשמה אוטומטית', @@ -807,6 +811,9 @@ export const heIL: LocalizationResource = { action__signOutAll: 'התנתק מכל החשבונות', }, userProfile: { + apiKeysPage: { + title: undefined, + }, backupCodePage: { actionLabel__copied: 'הועתק!', actionLabel__copy: 'העתק הכל', diff --git a/packages/localizations/src/hr-HR.ts b/packages/localizations/src/hr-HR.ts index 5c2adb772a7..9c1d8ef125d 100644 --- a/packages/localizations/src/hr-HR.ts +++ b/packages/localizations/src/hr-HR.ts @@ -14,9 +14,10 @@ import type { LocalizationResource } from '@clerk/types'; export const hrHR: LocalizationResource = { locale: 'hr-HR', - apiKey: { + apiKeys: { action__add: undefined, action__search: undefined, + detailsTitle__emptyRow: undefined, formHint: undefined, formTitle: undefined, menuAction__revoke: undefined, @@ -203,6 +204,9 @@ export const hrHR: LocalizationResource = { titleWithoutPersonal: 'Odaberite organizaciju', }, organizationProfile: { + apiKeysPage: { + title: undefined, + }, badge__automaticInvitation: 'Automatske pozivnice', badge__automaticSuggestion: 'Automatski prijedlozi', badge__manualInvitation: 'Bez automatskog učlanjenja', @@ -822,6 +826,9 @@ export const hrHR: LocalizationResource = { action__signOutAll: 'Odjavi se sa svih računa', }, userProfile: { + apiKeysPage: { + title: undefined, + }, backupCodePage: { actionLabel__copied: 'Kopirano!', actionLabel__copy: 'Kopiraj sve', diff --git a/packages/localizations/src/hu-HU.ts b/packages/localizations/src/hu-HU.ts index 9383d5a8e1d..69017da2dc7 100644 --- a/packages/localizations/src/hu-HU.ts +++ b/packages/localizations/src/hu-HU.ts @@ -14,9 +14,10 @@ import type { LocalizationResource } from '@clerk/types'; export const huHU: LocalizationResource = { locale: 'hu-HU', - apiKey: { + apiKeys: { action__add: undefined, action__search: undefined, + detailsTitle__emptyRow: undefined, formHint: undefined, formTitle: undefined, menuAction__revoke: undefined, @@ -203,6 +204,9 @@ export const huHU: LocalizationResource = { titleWithoutPersonal: 'Válassz egy szervezetet', }, organizationProfile: { + apiKeysPage: { + title: undefined, + }, badge__automaticInvitation: 'Automatikus meghívások', badge__automaticSuggestion: 'Automatikus javaslatok', badge__manualInvitation: 'Nincs automatikus felvétel', @@ -818,6 +822,9 @@ export const huHU: LocalizationResource = { action__signOutAll: 'Kijelentkezés minden fiókból', }, userProfile: { + apiKeysPage: { + title: undefined, + }, backupCodePage: { actionLabel__copied: 'Kimásolva!', actionLabel__copy: 'Az összes kimásolása', diff --git a/packages/localizations/src/id-ID.ts b/packages/localizations/src/id-ID.ts index 1948c06e08f..aac3c494f38 100644 --- a/packages/localizations/src/id-ID.ts +++ b/packages/localizations/src/id-ID.ts @@ -14,9 +14,10 @@ import type { LocalizationResource } from '@clerk/types'; export const idID: LocalizationResource = { locale: 'id-ID', - apiKey: { + apiKeys: { action__add: undefined, action__search: undefined, + detailsTitle__emptyRow: undefined, formHint: undefined, formTitle: undefined, menuAction__revoke: undefined, @@ -204,6 +205,9 @@ export const idID: LocalizationResource = { titleWithoutPersonal: 'Pilih organisasi', }, organizationProfile: { + apiKeysPage: { + title: undefined, + }, badge__automaticInvitation: 'Undangan otomatis', badge__automaticSuggestion: 'Saran otomatis', badge__manualInvitation: 'Tanpa pendaftaran otomatis', @@ -826,6 +830,9 @@ export const idID: LocalizationResource = { action__signOutAll: 'Keluar dari semua akun', }, userProfile: { + apiKeysPage: { + title: undefined, + }, backupCodePage: { actionLabel__copied: 'Disalin!', actionLabel__copy: 'Salin semua', diff --git a/packages/localizations/src/is-IS.ts b/packages/localizations/src/is-IS.ts index 83803eb61d6..da8daf03985 100644 --- a/packages/localizations/src/is-IS.ts +++ b/packages/localizations/src/is-IS.ts @@ -14,9 +14,10 @@ import type { LocalizationResource } from '@clerk/types'; export const isIS: LocalizationResource = { locale: 'is-IS', - apiKey: { + apiKeys: { action__add: undefined, action__search: undefined, + detailsTitle__emptyRow: undefined, formHint: undefined, formTitle: undefined, menuAction__revoke: undefined, @@ -204,6 +205,9 @@ export const isIS: LocalizationResource = { titleWithoutPersonal: 'Veldu samtök', }, organizationProfile: { + apiKeysPage: { + title: undefined, + }, badge__automaticInvitation: 'Sjálfvirk boð', badge__automaticSuggestion: 'Sjálfvirkar tillögur', badge__manualInvitation: 'Engin sjálfvirk skráning', @@ -820,6 +824,9 @@ export const isIS: LocalizationResource = { action__signOutAll: 'Skrá út af öllum reikningum', }, userProfile: { + apiKeysPage: { + title: undefined, + }, backupCodePage: { actionLabel__copied: 'Afritað!', actionLabel__copy: 'Afrita allt', diff --git a/packages/localizations/src/it-IT.ts b/packages/localizations/src/it-IT.ts index 77262fa73c1..aa21f51d207 100644 --- a/packages/localizations/src/it-IT.ts +++ b/packages/localizations/src/it-IT.ts @@ -14,9 +14,10 @@ import type { LocalizationResource } from '@clerk/types'; export const itIT: LocalizationResource = { locale: 'it-IT', - apiKey: { + apiKeys: { action__add: undefined, action__search: undefined, + detailsTitle__emptyRow: undefined, formHint: undefined, formTitle: undefined, menuAction__revoke: undefined, @@ -205,6 +206,9 @@ export const itIT: LocalizationResource = { titleWithoutPersonal: 'Scegli un’organizzazione', }, organizationProfile: { + apiKeysPage: { + title: undefined, + }, badge__automaticInvitation: 'Inviti automatici', badge__automaticSuggestion: 'Suggerimenti automatici', badge__manualInvitation: 'Nessuna iscrizione automatica', @@ -817,6 +821,9 @@ export const itIT: LocalizationResource = { action__signOutAll: 'Disconnetti da tutti gli accounts', }, userProfile: { + apiKeysPage: { + title: undefined, + }, backupCodePage: { actionLabel__copied: 'Copiati!', actionLabel__copy: 'Copia tutti', diff --git a/packages/localizations/src/ja-JP.ts b/packages/localizations/src/ja-JP.ts index 863252d2fe4..039c65045ff 100644 --- a/packages/localizations/src/ja-JP.ts +++ b/packages/localizations/src/ja-JP.ts @@ -14,9 +14,10 @@ import type { LocalizationResource } from '@clerk/types'; export const jaJP: LocalizationResource = { locale: 'ja-JP', - apiKey: { + apiKeys: { action__add: undefined, action__search: undefined, + detailsTitle__emptyRow: undefined, formHint: undefined, formTitle: undefined, menuAction__revoke: undefined, @@ -204,6 +205,9 @@ export const jaJP: LocalizationResource = { titleWithoutPersonal: '組織を選択', }, organizationProfile: { + apiKeysPage: { + title: undefined, + }, badge__automaticInvitation: '自動招待', badge__automaticSuggestion: '自動サジェスト', badge__manualInvitation: '自動登録なし', @@ -816,6 +820,9 @@ export const jaJP: LocalizationResource = { action__signOutAll: '全てのアカウントからサインアウト', }, userProfile: { + apiKeysPage: { + title: undefined, + }, backupCodePage: { actionLabel__copied: 'コピー済み!', actionLabel__copy: 'すべてコピー', diff --git a/packages/localizations/src/ko-KR.ts b/packages/localizations/src/ko-KR.ts index 96872d4642d..f5741390e15 100644 --- a/packages/localizations/src/ko-KR.ts +++ b/packages/localizations/src/ko-KR.ts @@ -14,9 +14,10 @@ import type { LocalizationResource } from '@clerk/types'; export const koKR: LocalizationResource = { locale: 'ko-KR', - apiKey: { + apiKeys: { action__add: undefined, action__search: undefined, + detailsTitle__emptyRow: undefined, formHint: undefined, formTitle: undefined, menuAction__revoke: undefined, @@ -204,6 +205,9 @@ export const koKR: LocalizationResource = { titleWithoutPersonal: 'Choose an organization', }, organizationProfile: { + apiKeysPage: { + title: undefined, + }, badge__automaticInvitation: 'Automatic invitations', badge__automaticSuggestion: 'Automatic suggestions', badge__manualInvitation: 'No automatic enrollment', @@ -810,6 +814,9 @@ export const koKR: LocalizationResource = { action__signOutAll: '모든 계정에서 로그아웃', }, userProfile: { + apiKeysPage: { + title: undefined, + }, backupCodePage: { actionLabel__copied: '복사 완료!', actionLabel__copy: '전체 복사', diff --git a/packages/localizations/src/mn-MN.ts b/packages/localizations/src/mn-MN.ts index d20404e2e92..9e5ab1b1a4f 100644 --- a/packages/localizations/src/mn-MN.ts +++ b/packages/localizations/src/mn-MN.ts @@ -14,9 +14,10 @@ import type { LocalizationResource } from '@clerk/types'; export const mnMN: LocalizationResource = { locale: 'mn-MN', - apiKey: { + apiKeys: { action__add: undefined, action__search: undefined, + detailsTitle__emptyRow: undefined, formHint: undefined, formTitle: undefined, menuAction__revoke: undefined, @@ -204,6 +205,9 @@ export const mnMN: LocalizationResource = { titleWithoutPersonal: 'Байгууллага сонгоно уу', }, organizationProfile: { + apiKeysPage: { + title: undefined, + }, badge__automaticInvitation: 'Автомат урилга', badge__automaticSuggestion: 'Автомат саналууд', badge__manualInvitation: 'Автомат бүртгэл байхгүй', @@ -817,6 +821,9 @@ export const mnMN: LocalizationResource = { action__signOutAll: 'Бүх бүртгэлээс гарна уу', }, userProfile: { + apiKeysPage: { + title: undefined, + }, backupCodePage: { actionLabel__copied: 'Хуулсан!', actionLabel__copy: 'Бүгдийг хуулах', diff --git a/packages/localizations/src/nb-NO.ts b/packages/localizations/src/nb-NO.ts index 6c38124a4e0..822575cf093 100644 --- a/packages/localizations/src/nb-NO.ts +++ b/packages/localizations/src/nb-NO.ts @@ -14,9 +14,10 @@ import type { LocalizationResource } from '@clerk/types'; export const nbNO: LocalizationResource = { locale: 'nb-NO', - apiKey: { + apiKeys: { action__add: undefined, action__search: undefined, + detailsTitle__emptyRow: undefined, formHint: undefined, formTitle: undefined, menuAction__revoke: undefined, @@ -204,6 +205,9 @@ export const nbNO: LocalizationResource = { titleWithoutPersonal: 'Velg en organiasjon', }, organizationProfile: { + apiKeysPage: { + title: undefined, + }, badge__automaticInvitation: 'Automatisk invitasjon', badge__automaticSuggestion: 'Automatisk forslag', badge__manualInvitation: 'Ingen automatisk registrering', @@ -816,6 +820,9 @@ export const nbNO: LocalizationResource = { action__signOutAll: 'Logg ut av alle kontoer', }, userProfile: { + apiKeysPage: { + title: undefined, + }, backupCodePage: { actionLabel__copied: 'Kopiert!', actionLabel__copy: 'Kopier alle', diff --git a/packages/localizations/src/nl-BE.ts b/packages/localizations/src/nl-BE.ts index da5eabb30ff..84c26b757c6 100644 --- a/packages/localizations/src/nl-BE.ts +++ b/packages/localizations/src/nl-BE.ts @@ -14,9 +14,10 @@ import type { LocalizationResource } from '@clerk/types'; export const nlBE: LocalizationResource = { locale: 'nl-NL', - apiKey: { + apiKeys: { action__add: undefined, action__search: undefined, + detailsTitle__emptyRow: undefined, formHint: undefined, formTitle: undefined, menuAction__revoke: undefined, @@ -203,6 +204,9 @@ export const nlBE: LocalizationResource = { titleWithoutPersonal: 'Kies een organisatie', }, organizationProfile: { + apiKeysPage: { + title: undefined, + }, badge__automaticInvitation: 'Automatische Uitnodiging', badge__automaticSuggestion: 'Automatische Suggesties', badge__manualInvitation: 'Geen automatische inschrijving', @@ -815,6 +819,9 @@ export const nlBE: LocalizationResource = { action__signOutAll: 'Uitloggen uit alle accounts', }, userProfile: { + apiKeysPage: { + title: undefined, + }, backupCodePage: { actionLabel__copied: 'Gekopieerd!', actionLabel__copy: 'Kopieer', diff --git a/packages/localizations/src/nl-NL.ts b/packages/localizations/src/nl-NL.ts index 3af6d924dcc..9a49ef66c2d 100644 --- a/packages/localizations/src/nl-NL.ts +++ b/packages/localizations/src/nl-NL.ts @@ -14,9 +14,10 @@ import type { LocalizationResource } from '@clerk/types'; export const nlNL: LocalizationResource = { locale: 'nl-NL', - apiKey: { + apiKeys: { action__add: undefined, action__search: undefined, + detailsTitle__emptyRow: undefined, formHint: undefined, formTitle: undefined, menuAction__revoke: undefined, @@ -203,6 +204,9 @@ export const nlNL: LocalizationResource = { titleWithoutPersonal: 'Kies een organisatie', }, organizationProfile: { + apiKeysPage: { + title: undefined, + }, badge__automaticInvitation: 'Automatische Uitnodiging', badge__automaticSuggestion: 'Automatische Suggesties', badge__manualInvitation: 'Geen automatische inschrijving', @@ -815,6 +819,9 @@ export const nlNL: LocalizationResource = { action__signOutAll: 'Uitloggen uit alle accounts', }, userProfile: { + apiKeysPage: { + title: undefined, + }, backupCodePage: { actionLabel__copied: 'Gekopieerd!', actionLabel__copy: 'Kopieer', diff --git a/packages/localizations/src/pl-PL.ts b/packages/localizations/src/pl-PL.ts index 90d8f7f9dc2..680b4609dba 100644 --- a/packages/localizations/src/pl-PL.ts +++ b/packages/localizations/src/pl-PL.ts @@ -14,9 +14,10 @@ import type { LocalizationResource } from '@clerk/types'; export const plPL: LocalizationResource = { locale: 'pl-PL', - apiKey: { + apiKeys: { action__add: undefined, action__search: undefined, + detailsTitle__emptyRow: undefined, formHint: undefined, formTitle: undefined, menuAction__revoke: undefined, @@ -204,6 +205,9 @@ export const plPL: LocalizationResource = { titleWithoutPersonal: 'Wybierz organizację', }, organizationProfile: { + apiKeysPage: { + title: undefined, + }, badge__automaticInvitation: 'Automatyczne zaproszenia', badge__automaticSuggestion: 'Automatyczne sugestie', badge__manualInvitation: 'Brak automatycznej rejestracji', @@ -825,6 +829,9 @@ export const plPL: LocalizationResource = { action__signOutAll: 'Wyloguj ze wszystkich kont', }, userProfile: { + apiKeysPage: { + title: undefined, + }, backupCodePage: { actionLabel__copied: 'Skopiowane!', actionLabel__copy: 'Skopiuj wszystkie', diff --git a/packages/localizations/src/pt-BR.ts b/packages/localizations/src/pt-BR.ts index b5c7d36d858..ae4075be543 100644 --- a/packages/localizations/src/pt-BR.ts +++ b/packages/localizations/src/pt-BR.ts @@ -14,9 +14,10 @@ import type { LocalizationResource } from '@clerk/types'; export const ptBR: LocalizationResource = { locale: 'pt-BR', - apiKey: { + apiKeys: { action__add: undefined, action__search: undefined, + detailsTitle__emptyRow: undefined, formHint: undefined, formTitle: undefined, menuAction__revoke: undefined, @@ -204,6 +205,9 @@ export const ptBR: LocalizationResource = { titleWithoutPersonal: 'Selecione uma organização', }, organizationProfile: { + apiKeysPage: { + title: undefined, + }, badge__automaticInvitation: 'Convites automáticos', badge__automaticSuggestion: 'Sugestões automáticas', badge__manualInvitation: 'Sem inscrição automática', @@ -821,6 +825,9 @@ export const ptBR: LocalizationResource = { action__signOutAll: 'Sair de todas as contas', }, userProfile: { + apiKeysPage: { + title: undefined, + }, backupCodePage: { actionLabel__copied: 'Copiado!', actionLabel__copy: 'Copiar tudo', diff --git a/packages/localizations/src/pt-PT.ts b/packages/localizations/src/pt-PT.ts index 21aa6316c18..23b370d4b84 100644 --- a/packages/localizations/src/pt-PT.ts +++ b/packages/localizations/src/pt-PT.ts @@ -14,9 +14,10 @@ import type { LocalizationResource } from '@clerk/types'; export const ptPT: LocalizationResource = { locale: 'pt-PT', - apiKey: { + apiKeys: { action__add: undefined, action__search: undefined, + detailsTitle__emptyRow: undefined, formHint: undefined, formTitle: undefined, menuAction__revoke: undefined, @@ -203,6 +204,9 @@ export const ptPT: LocalizationResource = { titleWithoutPersonal: 'Selecione uma organização', }, organizationProfile: { + apiKeysPage: { + title: undefined, + }, badge__automaticInvitation: 'Convites automáticos', badge__automaticSuggestion: 'Sugestões automáticas', badge__manualInvitation: 'Sem inscrição automática', @@ -815,6 +819,9 @@ export const ptPT: LocalizationResource = { action__signOutAll: 'Terminar sessão de todas as contas', }, userProfile: { + apiKeysPage: { + title: undefined, + }, backupCodePage: { actionLabel__copied: 'Copiado!', actionLabel__copy: 'Copiar tudo', diff --git a/packages/localizations/src/ro-RO.ts b/packages/localizations/src/ro-RO.ts index 21b5e69a54f..424b42bdf5b 100644 --- a/packages/localizations/src/ro-RO.ts +++ b/packages/localizations/src/ro-RO.ts @@ -14,9 +14,10 @@ import type { LocalizationResource } from '@clerk/types'; export const roRO: LocalizationResource = { locale: 'ro-RO', - apiKey: { + apiKeys: { action__add: undefined, action__search: undefined, + detailsTitle__emptyRow: undefined, formHint: undefined, formTitle: undefined, menuAction__revoke: undefined, @@ -205,6 +206,9 @@ export const roRO: LocalizationResource = { titleWithoutPersonal: 'Selectați o organizație', }, organizationProfile: { + apiKeysPage: { + title: undefined, + }, badge__automaticInvitation: 'Invitații automate', badge__automaticSuggestion: 'Sugestii automate', badge__manualInvitation: 'Fără înscriere automată', @@ -820,6 +824,9 @@ export const roRO: LocalizationResource = { action__signOutAll: 'Deconectați-vă din toate conturile', }, userProfile: { + apiKeysPage: { + title: undefined, + }, backupCodePage: { actionLabel__copied: 'Copiat!', actionLabel__copy: 'Copiați toate', diff --git a/packages/localizations/src/ru-RU.ts b/packages/localizations/src/ru-RU.ts index 81cceefd311..f8dae8f927e 100644 --- a/packages/localizations/src/ru-RU.ts +++ b/packages/localizations/src/ru-RU.ts @@ -14,9 +14,10 @@ import type { LocalizationResource } from '@clerk/types'; export const ruRU: LocalizationResource = { locale: 'ru-RU', - apiKey: { + apiKeys: { action__add: undefined, action__search: undefined, + detailsTitle__emptyRow: undefined, formHint: undefined, formTitle: undefined, menuAction__revoke: undefined, @@ -207,6 +208,9 @@ export const ruRU: LocalizationResource = { titleWithoutPersonal: 'Выбрать организацию', }, organizationProfile: { + apiKeysPage: { + title: undefined, + }, badge__automaticInvitation: 'Автоматические приглашения', badge__automaticSuggestion: 'Автоматические предложения', badge__manualInvitation: 'Нет автоматической регистрации', @@ -830,6 +834,9 @@ export const ruRU: LocalizationResource = { action__signOutAll: 'Выйти из всех учетных записей', }, userProfile: { + apiKeysPage: { + title: undefined, + }, backupCodePage: { actionLabel__copied: 'Скопировано!', actionLabel__copy: 'Копировать все', diff --git a/packages/localizations/src/sk-SK.ts b/packages/localizations/src/sk-SK.ts index e60905d9291..dc801f2d6fc 100644 --- a/packages/localizations/src/sk-SK.ts +++ b/packages/localizations/src/sk-SK.ts @@ -14,9 +14,10 @@ import type { LocalizationResource } from '@clerk/types'; export const skSK: LocalizationResource = { locale: 'sk-SK', - apiKey: { + apiKeys: { action__add: undefined, action__search: undefined, + detailsTitle__emptyRow: undefined, formHint: undefined, formTitle: undefined, menuAction__revoke: undefined, @@ -204,6 +205,9 @@ export const skSK: LocalizationResource = { titleWithoutPersonal: 'Choose an organization', }, organizationProfile: { + apiKeysPage: { + title: undefined, + }, badge__automaticInvitation: 'Automatic invitations', badge__automaticSuggestion: 'Automatic suggestions', badge__manualInvitation: 'No automatic enrollment', @@ -813,6 +817,9 @@ export const skSK: LocalizationResource = { action__signOutAll: 'Odhlásiť sa zo všetkých účtov', }, userProfile: { + apiKeysPage: { + title: undefined, + }, backupCodePage: { actionLabel__copied: 'Skopírované!', actionLabel__copy: 'Kopírovať všetko', diff --git a/packages/localizations/src/sr-RS.ts b/packages/localizations/src/sr-RS.ts index 807ed1db06d..7242ad33d6f 100644 --- a/packages/localizations/src/sr-RS.ts +++ b/packages/localizations/src/sr-RS.ts @@ -14,9 +14,10 @@ import type { LocalizationResource } from '@clerk/types'; export const srRS: LocalizationResource = { locale: 'sr-RS', - apiKey: { + apiKeys: { action__add: undefined, action__search: undefined, + detailsTitle__emptyRow: undefined, formHint: undefined, formTitle: undefined, menuAction__revoke: undefined, @@ -203,6 +204,9 @@ export const srRS: LocalizationResource = { titleWithoutPersonal: 'Izaberi organizaciju', }, organizationProfile: { + apiKeysPage: { + title: undefined, + }, badge__automaticInvitation: 'Automatske pozivnice', badge__automaticSuggestion: 'Automatski predlozi', badge__manualInvitation: 'Bez automatskog uključivanja', @@ -816,6 +820,9 @@ export const srRS: LocalizationResource = { action__signOutAll: 'Odjavi se sa svih naloga', }, userProfile: { + apiKeysPage: { + title: undefined, + }, backupCodePage: { actionLabel__copied: 'Kopirano!', actionLabel__copy: 'Kopiraj sve', diff --git a/packages/localizations/src/sv-SE.ts b/packages/localizations/src/sv-SE.ts index 5b47f67defb..a33d8fc6973 100644 --- a/packages/localizations/src/sv-SE.ts +++ b/packages/localizations/src/sv-SE.ts @@ -14,9 +14,10 @@ import type { LocalizationResource } from '@clerk/types'; export const svSE: LocalizationResource = { locale: 'sv-SE', - apiKey: { + apiKeys: { action__add: undefined, action__search: undefined, + detailsTitle__emptyRow: undefined, formHint: undefined, formTitle: undefined, menuAction__revoke: undefined, @@ -204,6 +205,9 @@ export const svSE: LocalizationResource = { titleWithoutPersonal: 'Välj en organisation', }, organizationProfile: { + apiKeysPage: { + title: undefined, + }, badge__automaticInvitation: 'Automatiska inbjudningar', badge__automaticSuggestion: 'Automatiska förslag', badge__manualInvitation: 'Ingen automatisk registrering', @@ -819,6 +823,9 @@ export const svSE: LocalizationResource = { action__signOutAll: 'Logga ut från alla konton', }, userProfile: { + apiKeysPage: { + title: undefined, + }, backupCodePage: { actionLabel__copied: 'Kopierat!', actionLabel__copy: 'Kopiera alla', diff --git a/packages/localizations/src/th-TH.ts b/packages/localizations/src/th-TH.ts index 5d7b89458cf..ba350368906 100644 --- a/packages/localizations/src/th-TH.ts +++ b/packages/localizations/src/th-TH.ts @@ -14,9 +14,10 @@ import type { LocalizationResource } from '@clerk/types'; export const thTH: LocalizationResource = { locale: 'th-TH', - apiKey: { + apiKeys: { action__add: undefined, action__search: undefined, + detailsTitle__emptyRow: undefined, formHint: undefined, formTitle: undefined, menuAction__revoke: undefined, @@ -203,6 +204,9 @@ export const thTH: LocalizationResource = { titleWithoutPersonal: 'เลือกองค์กร', }, organizationProfile: { + apiKeysPage: { + title: undefined, + }, badge__automaticInvitation: 'คำเชิญอัตโนมัติ', badge__automaticSuggestion: 'ข้อเสนอแนะอัตโนมัติ', badge__manualInvitation: 'ไม่มีการลงทะเบียนอัตโนมัติ', @@ -815,6 +819,9 @@ export const thTH: LocalizationResource = { action__signOutAll: 'ออกจากระบบทุกบัญชี', }, userProfile: { + apiKeysPage: { + title: undefined, + }, backupCodePage: { actionLabel__copied: 'คัดลอกแล้ว!', actionLabel__copy: 'คัดลอกทั้งหมด', diff --git a/packages/localizations/src/tr-TR.ts b/packages/localizations/src/tr-TR.ts index 4edfa2f1b45..1b1f4cdc49c 100644 --- a/packages/localizations/src/tr-TR.ts +++ b/packages/localizations/src/tr-TR.ts @@ -14,9 +14,10 @@ import type { LocalizationResource } from '@clerk/types'; export const trTR: LocalizationResource = { locale: 'tr-TR', - apiKey: { + apiKeys: { action__add: undefined, action__search: undefined, + detailsTitle__emptyRow: undefined, formHint: undefined, formTitle: undefined, menuAction__revoke: undefined, @@ -203,6 +204,9 @@ export const trTR: LocalizationResource = { titleWithoutPersonal: 'Bir organizasyon seçin', }, organizationProfile: { + apiKeysPage: { + title: undefined, + }, badge__automaticInvitation: 'Otomatik davetler', badge__automaticSuggestion: 'Otomatik öneriler', badge__manualInvitation: 'Davetler', @@ -819,6 +823,9 @@ export const trTR: LocalizationResource = { action__signOutAll: 'Tüm hesaplardan çıkış yap', }, userProfile: { + apiKeysPage: { + title: undefined, + }, backupCodePage: { actionLabel__copied: 'Kopyalandı!', actionLabel__copy: 'Hepsini kopyala', diff --git a/packages/localizations/src/uk-UA.ts b/packages/localizations/src/uk-UA.ts index 086becca707..e91b7cec85f 100644 --- a/packages/localizations/src/uk-UA.ts +++ b/packages/localizations/src/uk-UA.ts @@ -14,9 +14,10 @@ import type { LocalizationResource } from '@clerk/types'; export const ukUA: LocalizationResource = { locale: 'uk-UA', - apiKey: { + apiKeys: { action__add: undefined, action__search: undefined, + detailsTitle__emptyRow: undefined, formHint: undefined, formTitle: undefined, menuAction__revoke: undefined, @@ -204,6 +205,9 @@ export const ukUA: LocalizationResource = { titleWithoutPersonal: 'Choose an organization', }, organizationProfile: { + apiKeysPage: { + title: undefined, + }, badge__automaticInvitation: 'Automatic invitations', badge__automaticSuggestion: 'Automatic suggestions', badge__manualInvitation: 'No automatic enrollment', @@ -813,6 +817,9 @@ export const ukUA: LocalizationResource = { action__signOutAll: 'Вийти з усіх акаунтів', }, userProfile: { + apiKeysPage: { + title: undefined, + }, backupCodePage: { actionLabel__copied: 'Скопійовано!', actionLabel__copy: 'Копіювати все', diff --git a/packages/localizations/src/vi-VN.ts b/packages/localizations/src/vi-VN.ts index da0d3a62558..38ad4948cee 100644 --- a/packages/localizations/src/vi-VN.ts +++ b/packages/localizations/src/vi-VN.ts @@ -14,9 +14,10 @@ import type { LocalizationResource } from '@clerk/types'; export const viVN: LocalizationResource = { locale: 'vi-VN', - apiKey: { + apiKeys: { action__add: undefined, action__search: undefined, + detailsTitle__emptyRow: undefined, formHint: undefined, formTitle: undefined, menuAction__revoke: undefined, @@ -204,6 +205,9 @@ export const viVN: LocalizationResource = { titleWithoutPersonal: 'Choose an organization', }, organizationProfile: { + apiKeysPage: { + title: undefined, + }, badge__automaticInvitation: 'Automatic invitations', badge__automaticSuggestion: 'Automatic suggestions', badge__manualInvitation: 'No automatic enrollment', @@ -814,6 +818,9 @@ export const viVN: LocalizationResource = { action__signOutAll: 'Đăng xuất khỏi tất cả các tài khoản', }, userProfile: { + apiKeysPage: { + title: undefined, + }, backupCodePage: { actionLabel__copied: 'Đã sao chép!', actionLabel__copy: 'Sao chép tất cả', diff --git a/packages/localizations/src/zh-CN.ts b/packages/localizations/src/zh-CN.ts index 697b03079d8..1d1a0fbce6d 100644 --- a/packages/localizations/src/zh-CN.ts +++ b/packages/localizations/src/zh-CN.ts @@ -14,9 +14,10 @@ import type { LocalizationResource } from '@clerk/types'; export const zhCN: LocalizationResource = { locale: 'zh-CN', - apiKey: { + apiKeys: { action__add: undefined, action__search: undefined, + detailsTitle__emptyRow: undefined, formHint: undefined, formTitle: undefined, menuAction__revoke: undefined, @@ -202,6 +203,9 @@ export const zhCN: LocalizationResource = { titleWithoutPersonal: '选择一个组织', }, organizationProfile: { + apiKeysPage: { + title: undefined, + }, badge__automaticInvitation: '自动邀请', badge__automaticSuggestion: '自动建议', badge__manualInvitation: '无自动注册', @@ -801,6 +805,9 @@ export const zhCN: LocalizationResource = { action__signOutAll: '退出所有账户', }, userProfile: { + apiKeysPage: { + title: undefined, + }, backupCodePage: { actionLabel__copied: '已复制!', actionLabel__copy: '复制全部', diff --git a/packages/localizations/src/zh-TW.ts b/packages/localizations/src/zh-TW.ts index 28a776f8ef2..ea33046b427 100644 --- a/packages/localizations/src/zh-TW.ts +++ b/packages/localizations/src/zh-TW.ts @@ -14,9 +14,10 @@ import type { LocalizationResource } from '@clerk/types'; export const zhTW: LocalizationResource = { locale: 'zh-TW', - apiKey: { + apiKeys: { action__add: undefined, action__search: undefined, + detailsTitle__emptyRow: undefined, formHint: undefined, formTitle: undefined, menuAction__revoke: undefined, @@ -202,6 +203,9 @@ export const zhTW: LocalizationResource = { titleWithoutPersonal: '選擇一個組織', }, organizationProfile: { + apiKeysPage: { + title: undefined, + }, badge__automaticInvitation: '自動邀請', badge__automaticSuggestion: '自動建議', badge__manualInvitation: '不自動註冊', @@ -802,6 +806,9 @@ export const zhTW: LocalizationResource = { action__signOutAll: '退出所有帳戶', }, userProfile: { + apiKeysPage: { + title: undefined, + }, backupCodePage: { actionLabel__copied: '已複製!', actionLabel__copy: '複製全部', diff --git a/packages/types/src/localization.ts b/packages/types/src/localization.ts index fc22e5721b1..c877689f0ec 100644 --- a/packages/types/src/localization.ts +++ b/packages/types/src/localization.ts @@ -643,6 +643,7 @@ type _LocalizationResource = { }; apiKeysPage: { title: LocalizationValue; + detailsTitle__emptyRow: LocalizationValue; }; passkeyScreen: { title__rename: LocalizationValue; @@ -1008,6 +1009,7 @@ type _LocalizationResource = { }; apiKeysPage: { title: LocalizationValue; + detailsTitle__emptyRow: LocalizationValue; }; }; createOrganization: { @@ -1051,12 +1053,13 @@ type _LocalizationResource = { message: LocalizationValue; }; }; - apiKey: { + apiKeys: { formTitle: LocalizationValue; formHint: LocalizationValue; menuAction__revoke: LocalizationValue; action__search: LocalizationValue; action__add: LocalizationValue; + detailsTitle__emptyRow: LocalizationValue; }; }; From 0ee39fcc09e2f366d1dca47a4b1f5667fc6d2b3b Mon Sep 17 00:00:00 2001 From: wobsoriano Date: Thu, 15 May 2025 18:44:51 -0700 Subject: [PATCH 037/116] chore: add save button locale --- packages/clerk-js/sandbox/template.html | 2 +- .../clerk-js/src/ui/components/ApiKeys/CreateApiKeyForm.tsx | 1 + packages/localizations/src/ar-SA.ts | 1 + packages/localizations/src/be-BY.ts | 1 + packages/localizations/src/bg-BG.ts | 1 + packages/localizations/src/ca-ES.ts | 1 + packages/localizations/src/cs-CZ.ts | 1 + packages/localizations/src/da-DK.ts | 1 + packages/localizations/src/de-DE.ts | 1 + packages/localizations/src/el-GR.ts | 1 + packages/localizations/src/en-GB.ts | 1 + packages/localizations/src/en-US.ts | 1 + packages/localizations/src/es-ES.ts | 1 + packages/localizations/src/es-MX.ts | 1 + packages/localizations/src/fi-FI.ts | 1 + packages/localizations/src/fr-FR.ts | 1 + packages/localizations/src/he-IL.ts | 1 + packages/localizations/src/hr-HR.ts | 1 + packages/localizations/src/hu-HU.ts | 1 + packages/localizations/src/id-ID.ts | 1 + packages/localizations/src/is-IS.ts | 1 + packages/localizations/src/it-IT.ts | 1 + packages/localizations/src/ja-JP.ts | 1 + packages/localizations/src/ko-KR.ts | 1 + packages/localizations/src/mn-MN.ts | 1 + packages/localizations/src/nb-NO.ts | 1 + packages/localizations/src/nl-BE.ts | 1 + packages/localizations/src/nl-NL.ts | 1 + packages/localizations/src/pl-PL.ts | 1 + packages/localizations/src/pt-BR.ts | 1 + packages/localizations/src/pt-PT.ts | 1 + packages/localizations/src/ro-RO.ts | 1 + packages/localizations/src/ru-RU.ts | 1 + packages/localizations/src/sk-SK.ts | 1 + packages/localizations/src/sr-RS.ts | 1 + packages/localizations/src/sv-SE.ts | 1 + packages/localizations/src/th-TH.ts | 1 + packages/localizations/src/tr-TR.ts | 1 + packages/localizations/src/uk-UA.ts | 1 + packages/localizations/src/vi-VN.ts | 1 + packages/localizations/src/zh-CN.ts | 1 + packages/localizations/src/zh-TW.ts | 1 + packages/types/src/localization.ts | 1 + 43 files changed, 43 insertions(+), 1 deletion(-) diff --git a/packages/clerk-js/sandbox/template.html b/packages/clerk-js/sandbox/template.html index b1f51b01ff5..21794bdb864 100644 --- a/packages/clerk-js/sandbox/template.html +++ b/packages/clerk-js/sandbox/template.html @@ -265,7 +265,7 @@ class="relative isolate flex w-full rounded-md border border-white px-2 py-[0.4375rem] text-sm hover:bg-gray-50 aria-[current]:bg-gray-50" href="/api-keys" > - Manage API Keys + API Keys
  • diff --git a/packages/clerk-js/src/ui/components/ApiKeys/CreateApiKeyForm.tsx b/packages/clerk-js/src/ui/components/ApiKeys/CreateApiKeyForm.tsx index c8f172b26d5..6869fce5b4a 100644 --- a/packages/clerk-js/src/ui/components/ApiKeys/CreateApiKeyForm.tsx +++ b/packages/clerk-js/src/ui/components/ApiKeys/CreateApiKeyForm.tsx @@ -109,6 +109,7 @@ export const CreateApiKeyForm = ({ onCreate, isSubmitting }: CreateApiKeyFormPro {showAdvanced ? 'Hide' : 'Show'} advanced settings Date: Thu, 15 May 2025 19:03:37 -0700 Subject: [PATCH 038/116] chore: fix close form btn --- .../clerk-js/src/ui/components/ApiKeys/CreateApiKeyForm.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/clerk-js/src/ui/components/ApiKeys/CreateApiKeyForm.tsx b/packages/clerk-js/src/ui/components/ApiKeys/CreateApiKeyForm.tsx index 6869fce5b4a..f30f5d0ab80 100644 --- a/packages/clerk-js/src/ui/components/ApiKeys/CreateApiKeyForm.tsx +++ b/packages/clerk-js/src/ui/components/ApiKeys/CreateApiKeyForm.tsx @@ -111,7 +111,7 @@ export const CreateApiKeyForm = ({ onCreate, isSubmitting }: CreateApiKeyFormPro From 00d6d0b91b10d790c7d60340b66282a84644540a Mon Sep 17 00:00:00 2001 From: wobsoriano Date: Fri, 16 May 2025 13:02:26 -0700 Subject: [PATCH 039/116] chore: use description field and add custom expiration --- .../clerk-js/src/core/resources/ApiKey.ts | 6 +-- .../src/ui/components/ApiKeys/ApiKeys.tsx | 6 +-- .../components/ApiKeys/CreateApiKeyForm.tsx | 53 ++++++++++++++++++- packages/localizations/src/ar-SA.ts | 2 + packages/localizations/src/be-BY.ts | 2 + packages/localizations/src/bg-BG.ts | 2 + packages/localizations/src/ca-ES.ts | 2 + packages/localizations/src/cs-CZ.ts | 2 + packages/localizations/src/da-DK.ts | 2 + packages/localizations/src/de-DE.ts | 2 + packages/localizations/src/el-GR.ts | 2 + packages/localizations/src/en-GB.ts | 2 + packages/localizations/src/en-US.ts | 2 + packages/localizations/src/es-ES.ts | 2 + packages/localizations/src/es-MX.ts | 2 + packages/localizations/src/fi-FI.ts | 2 + packages/localizations/src/fr-FR.ts | 2 + packages/localizations/src/he-IL.ts | 2 + packages/localizations/src/hr-HR.ts | 2 + packages/localizations/src/hu-HU.ts | 2 + packages/localizations/src/id-ID.ts | 2 + packages/localizations/src/is-IS.ts | 2 + packages/localizations/src/it-IT.ts | 2 + packages/localizations/src/ja-JP.ts | 2 + packages/localizations/src/ko-KR.ts | 2 + packages/localizations/src/mn-MN.ts | 2 + packages/localizations/src/nb-NO.ts | 2 + packages/localizations/src/nl-BE.ts | 2 + packages/localizations/src/nl-NL.ts | 2 + packages/localizations/src/pl-PL.ts | 2 + packages/localizations/src/pt-BR.ts | 2 + packages/localizations/src/pt-PT.ts | 2 + packages/localizations/src/ro-RO.ts | 2 + packages/localizations/src/ru-RU.ts | 2 + packages/localizations/src/sk-SK.ts | 2 + packages/localizations/src/sr-RS.ts | 2 + packages/localizations/src/sv-SE.ts | 2 + packages/localizations/src/th-TH.ts | 2 + packages/localizations/src/tr-TR.ts | 2 + packages/localizations/src/uk-UA.ts | 2 + packages/localizations/src/vi-VN.ts | 2 + packages/localizations/src/zh-CN.ts | 2 + packages/localizations/src/zh-TW.ts | 2 + packages/types/src/apiKey.ts | 2 +- packages/types/src/clerk.ts | 2 +- packages/types/src/elementIds.ts | 3 +- packages/types/src/json.ts | 2 +- packages/types/src/localization.ts | 2 + 48 files changed, 144 insertions(+), 12 deletions(-) diff --git a/packages/clerk-js/src/core/resources/ApiKey.ts b/packages/clerk-js/src/core/resources/ApiKey.ts index 3f0090a180e..26f153d87e9 100644 --- a/packages/clerk-js/src/core/resources/ApiKey.ts +++ b/packages/clerk-js/src/core/resources/ApiKey.ts @@ -23,7 +23,7 @@ export class ApiKey extends BaseResource implements ApiKeyResource { expired!: boolean; expiration!: Date | null; createdBy!: string | null; - creationReason!: string | null; + description!: string | null; createdAt!: Date; updatedAt!: Date; @@ -48,7 +48,7 @@ export class ApiKey extends BaseResource implements ApiKeyResource { this.expired = data.expired; this.expiration = data.expiration ? unixEpochToDate(data.expiration) : null; this.createdBy = data.created_by; - this.creationReason = data.creation_reason; + this.description = data.description; this.updatedAt = unixEpochToDate(data.updated_at); this.createdAt = unixEpochToDate(data.created_at); return this; @@ -110,7 +110,7 @@ export class ApiKey extends BaseResource implements ApiKeyResource { type: params.type ?? 'api_key', name: params.name, subject: params.subject ?? this.clerk.organization?.id ?? this.clerk.user?.id ?? '', - creation_reason: params.creationReason, + description: params.description, seconds_until_expiration: params.secondsUntilExpiration, }), }) diff --git a/packages/clerk-js/src/ui/components/ApiKeys/ApiKeys.tsx b/packages/clerk-js/src/ui/components/ApiKeys/ApiKeys.tsx index 0f8d5cad413..cfce66edf07 100644 --- a/packages/clerk-js/src/ui/components/ApiKeys/ApiKeys.tsx +++ b/packages/clerk-js/src/ui/components/ApiKeys/ApiKeys.tsx @@ -11,7 +11,7 @@ import { MagnifyingGlass } from '../../icons'; import { ApiKeysTable } from './ApiKeysTable'; import type { OnCreateParams } from './CreateApiKeyForm'; import { CreateApiKeyForm } from './CreateApiKeyForm'; -import { getTimeLeftInSeconds, useApiKeys } from './useApiKeys'; +import { useApiKeys } from './useApiKeys'; export const ApiKeysInternal = ({ subject, perPage }: { subject: string; perPage?: number }) => { const { @@ -45,8 +45,8 @@ export const ApiKeysInternal = ({ subject, perPage }: { subject: string; perPage try { await createApiKey({ name: params.name, - creationReason: params.description, - secondsUntilExpiration: getTimeLeftInSeconds(params.expiration), + description: params.description, + secondsUntilExpiration: params.expiration, }); closeCardFn(); card.setError(undefined); diff --git a/packages/clerk-js/src/ui/components/ApiKeys/CreateApiKeyForm.tsx b/packages/clerk-js/src/ui/components/ApiKeys/CreateApiKeyForm.tsx index f30f5d0ab80..99f89923750 100644 --- a/packages/clerk-js/src/ui/components/ApiKeys/CreateApiKeyForm.tsx +++ b/packages/clerk-js/src/ui/components/ApiKeys/CreateApiKeyForm.tsx @@ -5,7 +5,7 @@ import { Form, FormButtons, FormContainer, SegmentedControl } from '../../elemen import { useActionContext } from '../../elements/Action/ActionRoot'; import { useFormControl } from '../../utils'; -export type OnCreateParams = { name: string; description?: string; expiration: Expiration }; +export type OnCreateParams = { name: string; description?: string; expiration: number | undefined }; interface CreateApiKeyFormProps { onCreate: (params: OnCreateParams, closeCardFn: () => void) => void; @@ -14,6 +14,39 @@ interface CreateApiKeyFormProps { export type Expiration = 'never' | '30d' | '90d' | 'custom'; +const getTimeLeftInSeconds = (expirationOption: Expiration, customDate?: string) => { + if (expirationOption === 'never') { + return; + } + + const now = new Date(); + let 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; +}; + +const getMinDate = () => { + const min = new Date(); + min.setDate(min.getDate() + 1); + return min.toISOString().split('T')[0]; +}; + export const CreateApiKeyForm = ({ onCreate, isSubmitting }: CreateApiKeyFormProps) => { const [showAdvanced, setShowAdvanced] = useState(false); const [expiration, setExpiration] = useState('never'); @@ -35,6 +68,13 @@ export const CreateApiKeyForm = ({ onCreate, isSubmitting }: CreateApiKeyFormPro isRequired: false, }); + const expirationDateField = useFormControl('expirationDate', '', { + type: 'date', + label: localizationKeys('formFieldLabel__apiKeyExpirationDate'), + placeholder: localizationKeys('formFieldInputPlaceholder__apiKeyExpirationDate'), + isRequired: false, + }); + const canSubmit = nameField.value.length > 2; const handleSubmit = (e: React.FormEvent) => { @@ -43,7 +83,7 @@ export const CreateApiKeyForm = ({ onCreate, isSubmitting }: CreateApiKeyFormPro { name: nameField.value, description: descriptionField.value || undefined, - expiration, + expiration: getTimeLeftInSeconds(expiration, expirationDateField.value), }, closeCardFn, ); @@ -96,6 +136,15 @@ export const CreateApiKeyForm = ({ onCreate, isSubmitting }: CreateApiKeyFormPro /> + {expiration === 'custom' && ( + + + + )} )} Date: Fri, 16 May 2025 16:17:57 -0700 Subject: [PATCH 040/116] chore: add last used field --- .../clerk-js/src/core/resources/ApiKey.ts | 2 ++ .../src/ui/components/ApiKeys/ApiKeys.tsx | 6 +---- .../ui/components/ApiKeys/ApiKeysTable.tsx | 3 ++- packages/clerk-js/src/utils/date.ts | 26 +++++++++++++++++++ packages/types/src/apiKey.ts | 1 + packages/types/src/json.ts | 1 + 6 files changed, 33 insertions(+), 6 deletions(-) diff --git a/packages/clerk-js/src/core/resources/ApiKey.ts b/packages/clerk-js/src/core/resources/ApiKey.ts index 26f153d87e9..1704bbc69e4 100644 --- a/packages/clerk-js/src/core/resources/ApiKey.ts +++ b/packages/clerk-js/src/core/resources/ApiKey.ts @@ -24,6 +24,7 @@ export class ApiKey extends BaseResource implements ApiKeyResource { expiration!: Date | null; createdBy!: string | null; description!: string | null; + lastUsedAt!: Date | null; createdAt!: Date; updatedAt!: Date; @@ -49,6 +50,7 @@ export class ApiKey extends BaseResource implements ApiKeyResource { this.expiration = data.expiration ? unixEpochToDate(data.expiration) : null; this.createdBy = data.created_by; this.description = data.description; + this.lastUsedAt = data.last_used_at ? unixEpochToDate(data.last_used_at) : null; this.updatedAt = unixEpochToDate(data.updated_at); this.createdAt = unixEpochToDate(data.created_at); return this; diff --git a/packages/clerk-js/src/ui/components/ApiKeys/ApiKeys.tsx b/packages/clerk-js/src/ui/components/ApiKeys/ApiKeys.tsx index cfce66edf07..edfcb6221c8 100644 --- a/packages/clerk-js/src/ui/components/ApiKeys/ApiKeys.tsx +++ b/packages/clerk-js/src/ui/components/ApiKeys/ApiKeys.tsx @@ -43,11 +43,7 @@ export const ApiKeysInternal = ({ subject, perPage }: { subject: string; perPage const handleCreateApiKey = async (params: OnCreateParams, closeCardFn: () => void) => { try { - await createApiKey({ - name: params.name, - description: params.description, - secondsUntilExpiration: params.expiration, - }); + await createApiKey(params); closeCardFn(); card.setError(undefined); } catch (err: any) { diff --git a/packages/clerk-js/src/ui/components/ApiKeys/ApiKeysTable.tsx b/packages/clerk-js/src/ui/components/ApiKeys/ApiKeysTable.tsx index fe283a2cee6..dab75e64f2f 100644 --- a/packages/clerk-js/src/ui/components/ApiKeys/ApiKeysTable.tsx +++ b/packages/clerk-js/src/ui/components/ApiKeys/ApiKeysTable.tsx @@ -3,6 +3,7 @@ import type { ApiKeyResource } from '@clerk/types'; import { useEffect, useState } from 'react'; import useSWR from 'swr'; +import { timeAgo } from '../../../utils/date'; import { Button, descriptors, @@ -140,7 +141,7 @@ export const ApiKeysTable = ({
  • - - - - setExpiration(value as Expiration)} - fullWidth + + - - - - - - - {expiration === 'custom' && ( - - - - )} + + + + setExpiration(value as Expiration)} + fullWidth + > + + + + + + + {expiration === 'custom' && ( + + + + )} + )}
    {apiKey.name} @@ -77,8 +71,8 @@ export const ApiKeysTable = ({ }} > void toggleSecret(apiKey.id)} - aria-label={revealedKeys[apiKey.id] ? 'Hide key' : 'Show key'} + aria-label={'Show key'} > - + {/* */} + - + {/* */} + @@ -103,7 +105,7 @@ export const ApiKeysTable = ({ // @ts-expect-error: TODO: Add locales label: 'Revoke key', isDestructive: true, - onClick: () => void revokeApiKey(apiKey.id), + onClick: () => onRevoke(apiKey.id), isDisabled: false, }, ]} diff --git a/packages/clerk-js/src/ui/components/ApiKeys/shared.ts b/packages/clerk-js/src/ui/components/ApiKeys/shared.ts index 653d740de66..129345cccf8 100644 --- a/packages/clerk-js/src/ui/components/ApiKeys/shared.ts +++ b/packages/clerk-js/src/ui/components/ApiKeys/shared.ts @@ -22,14 +22,13 @@ function getTimeLeftInSeconds(expirationOption: Expiration) { return diffInSecs; } -export function useApiKeys(subject: string, perPage: number = 5) { +export function useApiKeys({ subject, perPage = 5 }: { subject: string; perPage?: number }) { const clerk = useClerk(); const { data: apiKeys, isLoading, revalidate, } = useFetch(clerk.getApiKeys, { subject }, undefined, `api-key-source-${subject}`); - const [revealedKeys, setRevealedKeys] = useState>({}); const [page, setPage] = useState(1); const [search, setSearch] = useState(''); const itemsPerPage = perPage; @@ -41,19 +40,6 @@ export function useApiKeys(subject: string, perPage: number = 5) { const endingRow = Math.min(page * itemsPerPage, itemCount); const paginatedApiKeys = filteredApiKeys.slice(startingRow - 1, endingRow); - const toggleSecret = async (id: string) => { - setRevealedKeys(prev => { - if (prev[id]) { - return { ...prev, [id]: null }; - } - return prev; - }); - if (!revealedKeys[id]) { - const secret = await clerk.getApiKeySecret(id); - setRevealedKeys(prev => ({ ...prev, [id]: secret })); - } - }; - const handleCreate = async (params: { name: string; description?: string; @@ -78,8 +64,6 @@ export function useApiKeys(subject: string, perPage: number = 5) { return { apiKeys: paginatedApiKeys, isLoading: isLoading ?? false, - revealedKeys, - toggleSecret, revokeApiKey, search, setSearch, diff --git a/packages/clerk-js/src/ui/components/OrganizationProfile/OrganizationApiKeysPage.tsx b/packages/clerk-js/src/ui/components/OrganizationProfile/OrganizationApiKeysPage.tsx index 27bb37f0eda..790e065cb5d 100644 --- a/packages/clerk-js/src/ui/components/OrganizationProfile/OrganizationApiKeysPage.tsx +++ b/packages/clerk-js/src/ui/components/OrganizationProfile/OrganizationApiKeysPage.tsx @@ -1,37 +1,24 @@ import { useOrganization } from '@clerk/shared/react'; -import { ApiKeysContext, useApiKeysContext } from '../../contexts'; +import { ApiKeysContext } from '../../contexts'; import { Col } from '../../customizables'; import { Header } from '../../elements'; -import { ApiKeysInternal, CopyButton } from '../ApiKeys'; -import { useApiKeys } from '../ApiKeys/shared'; +import { ApiKeysInternal } from '../ApiKeys'; -function APIKeysPageInternal() { - const ctx = useApiKeysContext(); +export const OrganizationApiKeysPage = () => { const { organization } = useOrganization(); - const apiKeys = useApiKeys(organization?.id ?? '', ctx.perPage); - return ( - - - - ); -} -export const OrganizationApiKeysPage = () => { return (
    From ece97b3d76c0bbc6f0f2b09bd48a18b3396c60d3 Mon Sep 17 00:00:00 2001 From: wobsoriano Date: Mon, 12 May 2025 21:54:56 -0700 Subject: [PATCH 026/116] chore: add api key secret fetcher and clean up components --- .../ui/components/ApiKeys/ApiKeysTable.tsx | 74 +++++++++++-------- .../src/ui/components/ApiKeys/shared.ts | 14 ++++ 2 files changed, 59 insertions(+), 29 deletions(-) diff --git a/packages/clerk-js/src/ui/components/ApiKeys/ApiKeysTable.tsx b/packages/clerk-js/src/ui/components/ApiKeys/ApiKeysTable.tsx index e47cdc81e9f..06d05abfeca 100644 --- a/packages/clerk-js/src/ui/components/ApiKeys/ApiKeysTable.tsx +++ b/packages/clerk-js/src/ui/components/ApiKeys/ApiKeysTable.tsx @@ -1,12 +1,16 @@ import type { ApiKeyResource } from '@clerk/types'; +import { useState } from 'react'; +import { useApiKeySecret } from '../../components/ApiKeys/shared'; import { Button, Flex, Icon, Input, Spinner, Table, Tbody, Td, Text, Th, Thead, Tr } from '../../customizables'; import { ThreeDotsMenu } from '../../elements'; import { useClipboard } from '../../hooks'; -import { Clipboard, Eye } from '../../icons'; +import { Clipboard, Eye, EyeSlash } from '../../icons'; + +const CopySecretButton = ({ apiKeyID }: { apiKeyID: string }) => { + const { data: apiKeySecret } = useApiKeySecret(apiKeyID); + const { onCopy } = useClipboard(apiKeySecret ?? ''); -const CopyButton = ({ text }: { text: string }) => { - const { onCopy } = useClipboard(text); return ( + + ); +}; + export const ApiKeysTable = ({ rows, isLoading, @@ -77,33 +116,10 @@ export const ApiKeysTable = ({ - - - - - + + diff --git a/packages/clerk-js/src/ui/components/ApiKeys/shared.ts b/packages/clerk-js/src/ui/components/ApiKeys/shared.ts index 129345cccf8..8013d6cad0e 100644 --- a/packages/clerk-js/src/ui/components/ApiKeys/shared.ts +++ b/packages/clerk-js/src/ui/components/ApiKeys/shared.ts @@ -76,3 +76,17 @@ export function useApiKeys({ subject, perPage = 5 }: { subject: string; perPage? handleCreate, }; } + +export function useApiKeySecret(apiKeyID: string) { + const clerk = useClerk(); + + async function getApiKeySecret(apiKeyID?: string) { + if (apiKeyID) { + const secret = await clerk.getApiKeySecret(apiKeyID); + return secret; + } + return ''; + } + + return useFetch(getApiKeySecret, apiKeyID, undefined, `api-key-secret-source-${apiKeyID}`); +} From bdecb5572fdeb57969c09b882737c05fdf99a485 Mon Sep 17 00:00:00 2001 From: wobsoriano Date: Mon, 12 May 2025 22:14:09 -0700 Subject: [PATCH 027/116] chore: adjust table heading widths --- .../clerk-js/src/ui/components/ApiKeys/ApiKeysTable.tsx | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/packages/clerk-js/src/ui/components/ApiKeys/ApiKeysTable.tsx b/packages/clerk-js/src/ui/components/ApiKeys/ApiKeysTable.tsx index 06d05abfeca..bb43ce121fb 100644 --- a/packages/clerk-js/src/ui/components/ApiKeys/ApiKeysTable.tsx +++ b/packages/clerk-js/src/ui/components/ApiKeys/ApiKeysTable.tsx @@ -73,9 +73,9 @@ export const ApiKeysTable = ({
    NameLast usedLast used KeyActionsActions
    {apiKey.name} - + Created at{' '} {apiKey.createdAt.toLocaleDateString(undefined, { month: 'short', From 1e338ecebe2e39411dc41782fc64871e7fe9b806 Mon Sep 17 00:00:00 2001 From: wobsoriano Date: Tue, 13 May 2025 11:07:15 -0700 Subject: [PATCH 028/116] chore: improve secret fetching --- .../ui/components/ApiKeys/ApiKeysTable.tsx | 37 +++++++++++++------ .../src/ui/components/ApiKeys/shared.ts | 14 ------- 2 files changed, 26 insertions(+), 25 deletions(-) diff --git a/packages/clerk-js/src/ui/components/ApiKeys/ApiKeysTable.tsx b/packages/clerk-js/src/ui/components/ApiKeys/ApiKeysTable.tsx index bb43ce121fb..42b8ce909b4 100644 --- a/packages/clerk-js/src/ui/components/ApiKeys/ApiKeysTable.tsx +++ b/packages/clerk-js/src/ui/components/ApiKeys/ApiKeysTable.tsx @@ -1,23 +1,43 @@ +import { useClerk } from '@clerk/shared/react'; import type { ApiKeyResource } from '@clerk/types'; import { useState } from 'react'; -import { useApiKeySecret } from '../../components/ApiKeys/shared'; import { Button, Flex, Icon, Input, Spinner, Table, Tbody, Td, Text, Th, Thead, Tr } from '../../customizables'; import { ThreeDotsMenu } from '../../elements'; -import { useClipboard } from '../../hooks'; +import { useClipboard, useFetch } from '../../hooks'; import { Clipboard, Eye, EyeSlash } from '../../icons'; +const useApiKeySecret = ({ apiKeyID, enabled }: { apiKeyID: string; enabled: boolean }) => { + const clerk = useClerk(); + + const getSecret = async (apiKeyID?: string) => { + if (!apiKeyID) { + return ''; + } + const secret = await clerk.getApiKeySecret(apiKeyID); + return secret; + }; + + return useFetch(getSecret, enabled ? apiKeyID : undefined); +}; + const CopySecretButton = ({ apiKeyID }: { apiKeyID: string }) => { - const { data: apiKeySecret } = useApiKeySecret(apiKeyID); + const [enabled, setEnabled] = useState(false); + const { data: apiKeySecret } = useApiKeySecret({ apiKeyID, enabled }); const { onCopy } = useClipboard(apiKeySecret ?? ''); + const handleCopy = () => { + setEnabled(true); + onCopy(); + }; + return ( @@ -26,7 +46,7 @@ const CopySecretButton = ({ apiKeyID }: { apiKeyID: string }) => { const SecretInputWithToggle = ({ apiKeyID }: { apiKeyID: string }) => { const [revealed, setRevealed] = useState(false); - const { data: apiKeySecret } = useApiKeySecret(apiKeyID); + const { data: apiKeySecret } = useApiKeySecret({ apiKeyID, enabled: revealed }); return ( - - 3d ago - + 3d ago Date: Tue, 13 May 2025 11:13:12 -0700 Subject: [PATCH 029/116] chore: improve secret fetching --- .../src/ui/components/ApiKeys/ApiKeysTable.tsx | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/packages/clerk-js/src/ui/components/ApiKeys/ApiKeysTable.tsx b/packages/clerk-js/src/ui/components/ApiKeys/ApiKeysTable.tsx index 42b8ce909b4..ec59ca174e0 100644 --- a/packages/clerk-js/src/ui/components/ApiKeys/ApiKeysTable.tsx +++ b/packages/clerk-js/src/ui/components/ApiKeys/ApiKeysTable.tsx @@ -1,6 +1,6 @@ import { useClerk } from '@clerk/shared/react'; import type { ApiKeyResource } from '@clerk/types'; -import { useState } from 'react'; +import { useEffect, useState } from 'react'; import { Button, Flex, Icon, Input, Spinner, Table, Tbody, Td, Text, Th, Thead, Tr } from '../../customizables'; import { ThreeDotsMenu } from '../../elements'; @@ -26,10 +26,12 @@ const CopySecretButton = ({ apiKeyID }: { apiKeyID: string }) => { const { data: apiKeySecret } = useApiKeySecret({ apiKeyID, enabled }); const { onCopy } = useClipboard(apiKeySecret ?? ''); - const handleCopy = () => { - setEnabled(true); - onCopy(); - }; + useEffect(() => { + if (enabled && apiKeySecret) { + onCopy(); + setEnabled(false); + } + }, [enabled, apiKeySecret, onCopy]); return ( From 5faa5cfcb20f44e43bdf694cfe5492b8cbf67336 Mon Sep 17 00:00:00 2001 From: wobsoriano Date: Tue, 13 May 2025 15:02:49 -0700 Subject: [PATCH 030/116] chore: add locales --- .../ui/components/ApiKeys/ApiKeysTable.tsx | 45 ++++++++---- .../components/ApiKeys/CreateApiKeyForm.tsx | 19 +++-- .../src/ui/components/ApiKeys/shared.ts | 72 +++++++++++++++++++ packages/localizations/src/ar-SA.ts | 12 ++-- packages/localizations/src/be-BY.ts | 12 ++-- packages/localizations/src/bg-BG.ts | 12 ++-- packages/localizations/src/ca-ES.ts | 12 ++-- packages/localizations/src/cs-CZ.ts | 12 ++-- packages/localizations/src/da-DK.ts | 12 ++-- packages/localizations/src/de-DE.ts | 12 ++-- packages/localizations/src/el-GR.ts | 12 ++-- packages/localizations/src/en-GB.ts | 12 ++-- packages/localizations/src/en-US.ts | 12 ++-- packages/localizations/src/es-ES.ts | 12 ++-- packages/localizations/src/es-MX.ts | 12 ++-- packages/localizations/src/fi-FI.ts | 12 ++-- packages/localizations/src/fr-FR.ts | 12 ++-- packages/localizations/src/he-IL.ts | 12 ++-- packages/localizations/src/hr-HR.ts | 12 ++-- packages/localizations/src/hu-HU.ts | 12 ++-- packages/localizations/src/id-ID.ts | 12 ++-- packages/localizations/src/is-IS.ts | 12 ++-- packages/localizations/src/it-IT.ts | 12 ++-- packages/localizations/src/ja-JP.ts | 12 ++-- packages/localizations/src/ko-KR.ts | 12 ++-- packages/localizations/src/mn-MN.ts | 12 ++-- packages/localizations/src/nb-NO.ts | 12 ++-- packages/localizations/src/nl-BE.ts | 12 ++-- packages/localizations/src/nl-NL.ts | 12 ++-- packages/localizations/src/pl-PL.ts | 12 ++-- packages/localizations/src/pt-BR.ts | 12 ++-- packages/localizations/src/pt-PT.ts | 12 ++-- packages/localizations/src/ro-RO.ts | 12 ++-- packages/localizations/src/ru-RU.ts | 12 ++-- packages/localizations/src/sk-SK.ts | 12 ++-- packages/localizations/src/sr-RS.ts | 12 ++-- packages/localizations/src/sv-SE.ts | 12 ++-- packages/localizations/src/th-TH.ts | 12 ++-- packages/localizations/src/tr-TR.ts | 12 ++-- packages/localizations/src/uk-UA.ts | 12 ++-- packages/localizations/src/vi-VN.ts | 12 ++-- packages/localizations/src/zh-CN.ts | 12 ++-- packages/localizations/src/zh-TW.ts | 12 ++-- packages/types/src/localization.ts | 13 ++-- 44 files changed, 440 insertions(+), 189 deletions(-) diff --git a/packages/clerk-js/src/ui/components/ApiKeys/ApiKeysTable.tsx b/packages/clerk-js/src/ui/components/ApiKeys/ApiKeysTable.tsx index ec59ca174e0..d1bbe92e797 100644 --- a/packages/clerk-js/src/ui/components/ApiKeys/ApiKeysTable.tsx +++ b/packages/clerk-js/src/ui/components/ApiKeys/ApiKeysTable.tsx @@ -2,10 +2,25 @@ import { useClerk } from '@clerk/shared/react'; import type { ApiKeyResource } from '@clerk/types'; import { useEffect, useState } from 'react'; -import { Button, Flex, Icon, Input, Spinner, Table, Tbody, Td, Text, Th, Thead, Tr } from '../../customizables'; +import { + Button, + descriptors, + Flex, + Icon, + Input, + localizationKeys, + Spinner, + Table, + Tbody, + Td, + Text, + Th, + Thead, + Tr, +} from '../../customizables'; import { ThreeDotsMenu } from '../../elements'; import { useClipboard, useFetch } from '../../hooks'; -import { Clipboard, Eye, EyeSlash } from '../../icons'; +import { Check, Copy, Eye, EyeSlash } from '../../icons'; const useApiKeySecret = ({ apiKeyID, enabled }: { apiKeyID: string; enabled: boolean }) => { const clerk = useClerk(); @@ -24,7 +39,7 @@ const useApiKeySecret = ({ apiKeyID, enabled }: { apiKeyID: string; enabled: boo const CopySecretButton = ({ apiKeyID }: { apiKeyID: string }) => { const [enabled, setEnabled] = useState(false); const { data: apiKeySecret } = useApiKeySecret({ apiKeyID, enabled }); - const { onCopy } = useClipboard(apiKeySecret ?? ''); + const { onCopy, hasCopied } = useClipboard(apiKeySecret ?? ''); useEffect(() => { if (enabled && apiKeySecret) { @@ -36,12 +51,12 @@ const CopySecretButton = ({ apiKeyID }: { apiKeyID: string }) => { return ( ); }; @@ -74,6 +89,7 @@ const SecretInputWithToggle = ({ apiKeyID }: { apiKeyID: string }) => { sx={{ position: 'absolute', right: 0 }} aria-label={'Show key'} onClick={() => setRevealed(!revealed)} + focusRing={false} > @@ -91,13 +107,13 @@ export const ApiKeysTable = ({ onRevoke: (id: string) => void; }) => { return ( - +
    - + - + @@ -107,6 +123,7 @@ export const ApiKeysTable = ({ @@ -114,8 +131,11 @@ export const ApiKeysTable = ({ rows.map(apiKey => ( diff --git a/packages/clerk-js/src/ui/components/ApiKeys/CreateApiKeyForm.tsx b/packages/clerk-js/src/ui/components/ApiKeys/CreateApiKeyForm.tsx index bef9c08c188..e9d7c4f958d 100644 --- a/packages/clerk-js/src/ui/components/ApiKeys/CreateApiKeyForm.tsx +++ b/packages/clerk-js/src/ui/components/ApiKeys/CreateApiKeyForm.tsx @@ -20,15 +20,15 @@ export const CreateApiKeyForm = ({ onCreate }: CreateApiKeyFormProps) => { const nameField = useFormControl('name', '', { type: 'text', - label: 'Name', - placeholder: 'Enter your secret key name', + label: localizationKeys('formFieldLabel__apiKeyName'), + placeholder: localizationKeys('formFieldInputPlaceholder__apiKeyName'), isRequired: true, }); const descriptionField = useFormControl('description', '', { type: 'text', - label: 'Description', - placeholder: 'Enter a description for your API key', + label: localizationKeys('formFieldLabel__apiKeyDescription'), + placeholder: localizationKeys('formFieldInputPlaceholder__apiKeyDescription'), isRequired: false, }); @@ -46,8 +46,8 @@ export const CreateApiKeyForm = ({ onCreate }: CreateApiKeyFormProps) => { return ( @@ -61,10 +61,9 @@ export const CreateApiKeyForm = ({ onCreate }: CreateApiKeyFormProps) => { - Expiration - + variant='subtitle' + localizationKey={localizationKeys('formFieldLabel__apiKeyExpiration')} + /> Date: Tue, 13 May 2025 16:46:52 -0700 Subject: [PATCH 031/116] chore: action locales --- .../clerk-js/src/ui/components/ApiKeys/ApiKeys.tsx | 10 +++++++--- .../src/ui/components/ApiKeys/ApiKeysTable.tsx | 6 +++--- .../src/ui/components/ApiKeys/CreateApiKeyForm.tsx | 14 ++++++++------ packages/localizations/src/ar-SA.ts | 2 ++ packages/localizations/src/be-BY.ts | 2 ++ packages/localizations/src/bg-BG.ts | 2 ++ packages/localizations/src/ca-ES.ts | 2 ++ packages/localizations/src/cs-CZ.ts | 2 ++ packages/localizations/src/da-DK.ts | 2 ++ packages/localizations/src/de-DE.ts | 2 ++ packages/localizations/src/el-GR.ts | 2 ++ packages/localizations/src/en-GB.ts | 2 ++ packages/localizations/src/en-US.ts | 2 ++ packages/localizations/src/es-ES.ts | 2 ++ packages/localizations/src/es-MX.ts | 2 ++ packages/localizations/src/fi-FI.ts | 2 ++ packages/localizations/src/fr-FR.ts | 2 ++ packages/localizations/src/he-IL.ts | 2 ++ packages/localizations/src/hr-HR.ts | 2 ++ packages/localizations/src/hu-HU.ts | 2 ++ packages/localizations/src/id-ID.ts | 2 ++ packages/localizations/src/is-IS.ts | 2 ++ packages/localizations/src/it-IT.ts | 2 ++ packages/localizations/src/ja-JP.ts | 2 ++ packages/localizations/src/ko-KR.ts | 2 ++ packages/localizations/src/mn-MN.ts | 2 ++ packages/localizations/src/nb-NO.ts | 2 ++ packages/localizations/src/nl-BE.ts | 2 ++ packages/localizations/src/nl-NL.ts | 2 ++ packages/localizations/src/pl-PL.ts | 2 ++ packages/localizations/src/pt-BR.ts | 2 ++ packages/localizations/src/pt-PT.ts | 2 ++ packages/localizations/src/ro-RO.ts | 2 ++ packages/localizations/src/ru-RU.ts | 2 ++ packages/localizations/src/sk-SK.ts | 2 ++ packages/localizations/src/sr-RS.ts | 2 ++ packages/localizations/src/sv-SE.ts | 2 ++ packages/localizations/src/th-TH.ts | 2 ++ packages/localizations/src/tr-TR.ts | 2 ++ packages/localizations/src/uk-UA.ts | 2 ++ packages/localizations/src/vi-VN.ts | 2 ++ packages/localizations/src/zh-CN.ts | 2 ++ packages/localizations/src/zh-TW.ts | 2 ++ packages/types/src/localization.ts | 2 ++ 44 files changed, 100 insertions(+), 12 deletions(-) diff --git a/packages/clerk-js/src/ui/components/ApiKeys/ApiKeys.tsx b/packages/clerk-js/src/ui/components/ApiKeys/ApiKeys.tsx index a28ced232fd..2fd3208d782 100644 --- a/packages/clerk-js/src/ui/components/ApiKeys/ApiKeys.tsx +++ b/packages/clerk-js/src/ui/components/ApiKeys/ApiKeys.tsx @@ -1,7 +1,7 @@ import { useOrganization, useUser } from '@clerk/shared/react'; import { useApiKeysContext } from '../../contexts'; -import { Box, Button, Col, Flex, Flow, Icon } from '../../customizables'; +import { Box, Button, Col, Flex, Flow, Icon, localizationKeys, useLocalizations } from '../../customizables'; import { Card, InputWithIcon, Pagination, withCardStateProvider } from '../../elements'; import { Action } from '../../elements/Action'; import { MagnifyingGlass } from '../../icons'; @@ -24,6 +24,7 @@ export const ApiKeysInternal = ({ subject }: { subject: string }) => { endingRow, handleCreate, } = useApiKeys({ subject }); + const { t } = useLocalizations(); return ( @@ -34,7 +35,7 @@ export const ApiKeysInternal = ({ subject }: { subject: string }) => { > } value={search} onChange={e => { @@ -44,7 +45,10 @@ export const ApiKeysInternal = ({ subject }: { subject: string }) => { /> - +
    NameLast usedLast used KeyActionsActions
    - {apiKey.name} - + {apiKey.name} + Created at{' '} {apiKey.createdAt.toLocaleDateString(undefined, { month: 'short', @@ -140,14 +160,11 @@ export const ApiKeysTable = ({ onRevoke(apiKey.id), - isDisabled: false, }, ]} - elementId={'member'} />
    +
    - + - + diff --git a/packages/clerk-js/src/ui/components/ApiKeys/CreateApiKeyForm.tsx b/packages/clerk-js/src/ui/components/ApiKeys/CreateApiKeyForm.tsx index e9d7c4f958d..3ff4ffae39b 100644 --- a/packages/clerk-js/src/ui/components/ApiKeys/CreateApiKeyForm.tsx +++ b/packages/clerk-js/src/ui/components/ApiKeys/CreateApiKeyForm.tsx @@ -1,6 +1,6 @@ import React, { useState } from 'react'; -import { Button, Col, Flex, localizationKeys, Text } from '../../customizables'; +import { Button, Col, Flex, FormLabel, localizationKeys, Text } from '../../customizables'; import { Form, FormButtons, FormContainer, SegmentedControl } from '../../elements'; import { useActionContext } from '../../elements/Action/ActionRoot'; import { useFormControl } from '../../utils'; @@ -59,11 +59,13 @@ export const CreateApiKeyForm = ({ onCreate }: CreateApiKeyFormProps) => { - + + + Date: Tue, 13 May 2025 17:11:47 -0700 Subject: [PATCH 032/116] chore: add locales to api keys page in user and org profile --- .../clerk-js/src/ui/components/ApiKeys/ApiKeys.tsx | 11 +++++++---- .../components/ApiKeys/{shared.ts => useApiKeys.ts} | 4 ++-- .../OrganizationProfile/OrganizationApiKeysPage.tsx | 5 ++--- .../src/ui/components/UserProfile/ApiKeysPage.tsx | 5 ++--- packages/localizations/src/en-US.ts | 6 ++++++ packages/types/src/localization.ts | 6 ++++++ 6 files changed, 25 insertions(+), 12 deletions(-) rename packages/clerk-js/src/ui/components/ApiKeys/{shared.ts => useApiKeys.ts} (97%) diff --git a/packages/clerk-js/src/ui/components/ApiKeys/ApiKeys.tsx b/packages/clerk-js/src/ui/components/ApiKeys/ApiKeys.tsx index 2fd3208d782..ca2e70124cf 100644 --- a/packages/clerk-js/src/ui/components/ApiKeys/ApiKeys.tsx +++ b/packages/clerk-js/src/ui/components/ApiKeys/ApiKeys.tsx @@ -7,9 +7,9 @@ import { Action } from '../../elements/Action'; import { MagnifyingGlass } from '../../icons'; import { ApiKeysTable } from './ApiKeysTable'; import { CreateApiKeyForm } from './CreateApiKeyForm'; -import { useApiKeys } from './shared'; +import { useApiKeys } from './useApiKeys'; -export const ApiKeysInternal = ({ subject }: { subject: string }) => { +export const ApiKeysInternal = ({ subject, perPage }: { subject: string; perPage?: number }) => { const { apiKeys, isLoading, @@ -23,7 +23,7 @@ export const ApiKeysInternal = ({ subject }: { subject: string }) => { startingRow, endingRow, handleCreate, - } = useApiKeys({ subject }); + } = useApiKeys({ subject, perPage }); const { t } = useLocalizations(); return ( @@ -86,7 +86,10 @@ export const ApiKeys = withCardStateProvider(() => { - + diff --git a/packages/clerk-js/src/ui/components/ApiKeys/shared.ts b/packages/clerk-js/src/ui/components/ApiKeys/useApiKeys.ts similarity index 97% rename from packages/clerk-js/src/ui/components/ApiKeys/shared.ts rename to packages/clerk-js/src/ui/components/ApiKeys/useApiKeys.ts index ab36330af46..6160bfdf70d 100644 --- a/packages/clerk-js/src/ui/components/ApiKeys/shared.ts +++ b/packages/clerk-js/src/ui/components/ApiKeys/useApiKeys.ts @@ -23,7 +23,7 @@ function getTimeLeftInSeconds(expirationOption: Expiration) { return diffInSecs; } -export function useApiKeys({ subject, perPage = 5 }: { subject: string; perPage?: number }) { +export const useApiKeys = ({ subject, perPage = 5 }: { subject: string; perPage?: number }) => { const clerk = useClerk(); const { data: apiKeys, @@ -76,7 +76,7 @@ export function useApiKeys({ subject, perPage = 5 }: { subject: string; perPage? endingRow, handleCreate, }; -} +}; export const testFakeData = [ { diff --git a/packages/clerk-js/src/ui/components/OrganizationProfile/OrganizationApiKeysPage.tsx b/packages/clerk-js/src/ui/components/OrganizationProfile/OrganizationApiKeysPage.tsx index 790e065cb5d..0b95db159cf 100644 --- a/packages/clerk-js/src/ui/components/OrganizationProfile/OrganizationApiKeysPage.tsx +++ b/packages/clerk-js/src/ui/components/OrganizationProfile/OrganizationApiKeysPage.tsx @@ -1,7 +1,7 @@ import { useOrganization } from '@clerk/shared/react'; import { ApiKeysContext } from '../../contexts'; -import { Col } from '../../customizables'; +import { Col, localizationKeys } from '../../customizables'; import { Header } from '../../elements'; import { ApiKeysInternal } from '../ApiKeys'; @@ -12,8 +12,7 @@ export const OrganizationApiKeysPage = () => { diff --git a/packages/clerk-js/src/ui/components/UserProfile/ApiKeysPage.tsx b/packages/clerk-js/src/ui/components/UserProfile/ApiKeysPage.tsx index 9c546430613..e4b421afef5 100644 --- a/packages/clerk-js/src/ui/components/UserProfile/ApiKeysPage.tsx +++ b/packages/clerk-js/src/ui/components/UserProfile/ApiKeysPage.tsx @@ -1,7 +1,7 @@ import { useUser } from '@clerk/shared/react'; import { ApiKeysContext } from '../../contexts'; -import { Col } from '../../customizables'; +import { Col, localizationKeys } from '../../customizables'; import { Header } from '../../elements'; import { ApiKeysInternal } from '../ApiKeys'; @@ -12,8 +12,7 @@ export const ApiKeysPage = () => { diff --git a/packages/localizations/src/en-US.ts b/packages/localizations/src/en-US.ts index 9e46a758376..5244986ed1a 100644 --- a/packages/localizations/src/en-US.ts +++ b/packages/localizations/src/en-US.ts @@ -292,6 +292,9 @@ export const enUS: LocalizationResource = { headerTitle__requests: 'Requests', }, }, + apiKeysPage: { + title: 'API Keys', + }, navbar: { billing: 'Billing', description: 'Manage your organization.', @@ -913,6 +916,9 @@ export const enUS: LocalizationResource = { title: 'Add email address', verifyTitle: 'Verify email address', }, + apiKeysPage: { + title: 'API Keys', + }, formButtonPrimary__add: 'Add', formButtonPrimary__continue: 'Continue', formButtonPrimary__finish: 'Finish', diff --git a/packages/types/src/localization.ts b/packages/types/src/localization.ts index da542644dc2..fc22e5721b1 100644 --- a/packages/types/src/localization.ts +++ b/packages/types/src/localization.ts @@ -641,6 +641,9 @@ type _LocalizationResource = { successMessage: LocalizationValue; }; }; + apiKeysPage: { + title: LocalizationValue; + }; passkeyScreen: { title__rename: LocalizationValue; subtitle__rename: LocalizationValue; @@ -1003,6 +1006,9 @@ type _LocalizationResource = { noPermissionsToManageBilling: LocalizationValue; }; }; + apiKeysPage: { + title: LocalizationValue; + }; }; createOrganization: { title: LocalizationValue; From 5523b1af1bd2f8192e747e43511830730aded4c3 Mon Sep 17 00:00:00 2001 From: wobsoriano Date: Thu, 15 May 2025 12:43:33 -0700 Subject: [PATCH 033/116] chore: switch to swr for fetching --- .../src/ui/components/ApiKeys/ApiKeysTable.tsx | 13 +++---------- .../src/ui/components/ApiKeys/useApiKeys.ts | 10 +++++----- 2 files changed, 8 insertions(+), 15 deletions(-) diff --git a/packages/clerk-js/src/ui/components/ApiKeys/ApiKeysTable.tsx b/packages/clerk-js/src/ui/components/ApiKeys/ApiKeysTable.tsx index 91ab0947629..5c99419f6b6 100644 --- a/packages/clerk-js/src/ui/components/ApiKeys/ApiKeysTable.tsx +++ b/packages/clerk-js/src/ui/components/ApiKeys/ApiKeysTable.tsx @@ -1,6 +1,7 @@ import { useClerk } from '@clerk/shared/react'; import type { ApiKeyResource } from '@clerk/types'; import { useEffect, useState } from 'react'; +import useSWR from 'swr'; import { Button, @@ -19,21 +20,13 @@ import { Tr, } from '../../customizables'; import { ThreeDotsMenu } from '../../elements'; -import { useClipboard, useFetch } from '../../hooks'; +import { useClipboard } from '../../hooks'; import { Check, Copy, Eye, EyeSlash } from '../../icons'; const useApiKeySecret = ({ apiKeyID, enabled }: { apiKeyID: string; enabled: boolean }) => { const clerk = useClerk(); - const getSecret = async (apiKeyID?: string) => { - if (!apiKeyID) { - return ''; - } - const secret = await clerk.getApiKeySecret(apiKeyID); - return secret; - }; - - return useFetch(getSecret, enabled ? apiKeyID : undefined); + return useSWR(enabled ? ['api-key-secret', apiKeyID] : null, ([_, id]) => clerk.getApiKeySecret(id)); }; const CopySecretButton = ({ apiKeyID }: { apiKeyID: string }) => { diff --git a/packages/clerk-js/src/ui/components/ApiKeys/useApiKeys.ts b/packages/clerk-js/src/ui/components/ApiKeys/useApiKeys.ts index 6160bfdf70d..99618e37f91 100644 --- a/packages/clerk-js/src/ui/components/ApiKeys/useApiKeys.ts +++ b/packages/clerk-js/src/ui/components/ApiKeys/useApiKeys.ts @@ -1,8 +1,8 @@ import { useClerk } from '@clerk/shared/react'; import { useState } from 'react'; +import useSWR from 'swr'; import { unixEpochToDate } from '../../../utils/date'; -import { useFetch } from '../../hooks'; import type { Expiration } from './CreateApiKeyForm'; function getTimeLeftInSeconds(expirationOption: Expiration) { @@ -28,8 +28,8 @@ export const useApiKeys = ({ subject, perPage = 5 }: { subject: string; perPage? const { data: apiKeys, isLoading, - revalidate, - } = useFetch(clerk.getApiKeys, { subject }, undefined, `api-key-source-${subject}`); + mutate, + } = useSWR(['api-keys', subject], ([_, userIdOrOrgId]) => clerk.getApiKeys({ subject: userIdOrOrgId })); const [page, setPage] = useState(1); const [search, setSearch] = useState(''); const itemsPerPage = perPage; @@ -53,13 +53,13 @@ export const useApiKeys = ({ subject, perPage = 5 }: { subject: string; perPage? secondsUntilExpiration: getTimeLeftInSeconds(params.expiration), }); params.closeFn(); - revalidate(); + void mutate(); }; const revokeApiKey = async (apiKeyID: string) => { await clerk.revokeApiKey({ apiKeyID, revocationReason: 'Revoked by user' }); setPage(1); - revalidate(); + void mutate(); }; return { From 944ec76071d8719a54f350e5d2d97ccee79ac5b5 Mon Sep 17 00:00:00 2001 From: wobsoriano Date: Thu, 15 May 2025 14:52:42 -0700 Subject: [PATCH 034/116] chore: clean up fetchers and mutations --- .../src/ui/components/ApiKeys/ApiKeys.tsx | 54 ++++++- .../components/ApiKeys/CreateApiKeyForm.tsx | 24 +-- .../src/ui/components/ApiKeys/useApiKeys.ts | 145 +++++------------- packages/localizations/src/en-US.ts | 2 +- 4 files changed, 100 insertions(+), 125 deletions(-) diff --git a/packages/clerk-js/src/ui/components/ApiKeys/ApiKeys.tsx b/packages/clerk-js/src/ui/components/ApiKeys/ApiKeys.tsx index ca2e70124cf..4ae25c23966 100644 --- a/packages/clerk-js/src/ui/components/ApiKeys/ApiKeys.tsx +++ b/packages/clerk-js/src/ui/components/ApiKeys/ApiKeys.tsx @@ -1,4 +1,7 @@ -import { useOrganization, useUser } from '@clerk/shared/react'; +import { isClerkAPIResponseError } from '@clerk/shared/error'; +import { useClerk, useOrganization, useUser } from '@clerk/shared/react'; +import type { CreateApiKeyParams } from '@clerk/types'; +import useSWRMutation from 'swr/mutation'; import { useApiKeysContext } from '../../contexts'; import { Box, Button, Col, Flex, Flow, Icon, localizationKeys, useLocalizations } from '../../customizables'; @@ -6,14 +9,14 @@ import { Card, InputWithIcon, Pagination, withCardStateProvider } from '../../el import { Action } from '../../elements/Action'; import { MagnifyingGlass } from '../../icons'; import { ApiKeysTable } from './ApiKeysTable'; +import type { OnCreateParams } from './CreateApiKeyForm'; import { CreateApiKeyForm } from './CreateApiKeyForm'; -import { useApiKeys } from './useApiKeys'; +import { getTimeLeftInSeconds, useApiKeys } from './useApiKeys'; export const ApiKeysInternal = ({ subject, perPage }: { subject: string; perPage?: number }) => { const { apiKeys, isLoading, - revokeApiKey, search, setSearch, page, @@ -22,9 +25,40 @@ export const ApiKeysInternal = ({ subject, perPage }: { subject: string; perPage itemCount, startingRow, endingRow, - handleCreate, + mutate: mutateApiKeys, + cacheKey, } = useApiKeys({ subject, perPage }); + const { trigger: createApiKey, isMutating } = useSWRMutation( + cacheKey, + (_, { arg }: { arg: CreateApiKeyParams }) => clerk.createApiKey(arg), + { + throwOnError: false, + onError(err) { + if (isClerkAPIResponseError(err)) { + console.log(err.errors); + console.log(err.message); + console.log(err.name); + } + }, + }, + ); const { t } = useLocalizations(); + const clerk = useClerk(); + + const handleRevokeApiKey = async (id: string) => { + await clerk.revokeApiKey({ apiKeyID: id }); + void mutateApiKeys(); + setPage(1); + }; + + const handleCreateApiKey = async (params: OnCreateParams, closeCardFn: () => void) => { + await createApiKey({ + name: params.name, + creationReason: params.description, + secondsUntilExpiration: getTimeLeftInSeconds(params.expiration), + }); + closeCardFn(); + }; return ( @@ -44,7 +78,10 @@ export const ApiKeysInternal = ({ subject, perPage }: { subject: string; perPage }} /> - + @@ -153,7 +155,7 @@ export const ApiKeysTable = ({ onRevoke(apiKey.id), }, @@ -167,3 +169,20 @@ export const ApiKeysTable = ({
    NameLast usedLast used KeyActionsActions
    ); }; + +const EmptyRow = () => { + return ( +
    + +
    - 3d ago + {apiKey.lastUsedAt ? timeAgo(apiKey.lastUsedAt) : ''} Date: Fri, 16 May 2025 20:10:29 -0700 Subject: [PATCH 041/116] chore: remove unused function --- .../src/ui/components/ApiKeys/useApiKeys.ts | 20 ------------------- 1 file changed, 20 deletions(-) diff --git a/packages/clerk-js/src/ui/components/ApiKeys/useApiKeys.ts b/packages/clerk-js/src/ui/components/ApiKeys/useApiKeys.ts index d26af3b63bb..124cdb52bf9 100644 --- a/packages/clerk-js/src/ui/components/ApiKeys/useApiKeys.ts +++ b/packages/clerk-js/src/ui/components/ApiKeys/useApiKeys.ts @@ -2,26 +2,6 @@ import { useClerk } from '@clerk/shared/react'; import { useState } from 'react'; import useSWR from 'swr'; -import type { Expiration } from './CreateApiKeyForm'; - -export function getTimeLeftInSeconds(expirationOption: Expiration) { - if (expirationOption === 'never') { - return; - } - const now = new Date(); - const future = new Date(now); - if (expirationOption === '30d') { - future.setDate(future.getDate() + 30); - } else if (expirationOption === '90d') { - future.setDate(future.getDate() + 90); - } else { - throw new Error('TODO: Improve time handling'); - } - const diffInMs = future.getTime() - now.getTime(); - const diffInSecs = Math.floor(diffInMs / 1000); - return diffInSecs; -} - export const useApiKeys = ({ subject, perPage = 5 }: { subject: string; perPage?: number }) => { const clerk = useClerk(); const cacheKey = { From 0f05f25512d61fa2fce8320ecc891e0c5ef30c9b Mon Sep 17 00:00:00 2001 From: wobsoriano Date: Fri, 16 May 2025 20:25:07 -0700 Subject: [PATCH 042/116] chore: add card width --- packages/clerk-js/src/ui/components/ApiKeys/ApiKeys.tsx | 9 ++++++++- .../clerk-js/src/ui/components/ApiKeys/ApiKeysTable.tsx | 4 ++-- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/packages/clerk-js/src/ui/components/ApiKeys/ApiKeys.tsx b/packages/clerk-js/src/ui/components/ApiKeys/ApiKeys.tsx index edfcb6221c8..5d71f1c4980 100644 --- a/packages/clerk-js/src/ui/components/ApiKeys/ApiKeys.tsx +++ b/packages/clerk-js/src/ui/components/ApiKeys/ApiKeys.tsx @@ -119,7 +119,14 @@ export const ApiKeys = withCardStateProvider(() => { return ( - + ({ + width: t.sizes.$220, + maxWidth: `calc(100vw - ${t.sizes.$8})`, + }), + ]} + > void; }) => { return ( - +
    ({ tableLayout: 'fixed' })}> @@ -141,7 +141,7 @@ export const ApiKeysTable = ({ + + + + + + )) + )} + +
    Name - {apiKey.lastUsedAt ? timeAgo(apiKey.lastUsedAt) : ''} + {apiKey.lastUsedAt ? timeAgo(apiKey.lastUsedAt) : '-'} Date: Fri, 16 May 2025 20:25:43 -0700 Subject: [PATCH 043/116] chore: add card width --- packages/clerk-js/src/ui/components/ApiKeys/ApiKeysTable.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/clerk-js/src/ui/components/ApiKeys/ApiKeysTable.tsx b/packages/clerk-js/src/ui/components/ApiKeys/ApiKeysTable.tsx index a89d584d39f..eead023b7a4 100644 --- a/packages/clerk-js/src/ui/components/ApiKeys/ApiKeysTable.tsx +++ b/packages/clerk-js/src/ui/components/ApiKeys/ApiKeysTable.tsx @@ -101,7 +101,7 @@ export const ApiKeysTable = ({ onRevoke: (id: string) => void; }) => { return ( - ({ tableLayout: 'fixed' })}> +
    From 670887307d3df2a63e0af9ded96fb181a566e404 Mon Sep 17 00:00:00 2001 From: wobsoriano Date: Sun, 18 May 2025 20:41:45 -0700 Subject: [PATCH 044/116] chore: add custom page locales --- packages/clerk-js/src/ui/components/ApiKeys/ApiKeysTable.tsx | 2 +- packages/clerk-js/src/ui/utils/createCustomPages.tsx | 4 ++-- packages/localizations/src/ar-SA.ts | 2 ++ packages/localizations/src/be-BY.ts | 2 ++ packages/localizations/src/bg-BG.ts | 2 ++ packages/localizations/src/ca-ES.ts | 2 ++ packages/localizations/src/cs-CZ.ts | 2 ++ packages/localizations/src/da-DK.ts | 2 ++ packages/localizations/src/de-DE.ts | 2 ++ packages/localizations/src/el-GR.ts | 2 ++ packages/localizations/src/en-GB.ts | 2 ++ packages/localizations/src/en-US.ts | 2 ++ packages/localizations/src/es-ES.ts | 2 ++ packages/localizations/src/es-MX.ts | 2 ++ packages/localizations/src/fi-FI.ts | 2 ++ packages/localizations/src/fr-FR.ts | 2 ++ packages/localizations/src/he-IL.ts | 2 ++ packages/localizations/src/hr-HR.ts | 2 ++ packages/localizations/src/hu-HU.ts | 2 ++ packages/localizations/src/id-ID.ts | 2 ++ packages/localizations/src/is-IS.ts | 2 ++ packages/localizations/src/it-IT.ts | 2 ++ packages/localizations/src/ja-JP.ts | 2 ++ packages/localizations/src/ko-KR.ts | 2 ++ packages/localizations/src/mn-MN.ts | 2 ++ packages/localizations/src/nb-NO.ts | 2 ++ packages/localizations/src/nl-BE.ts | 2 ++ packages/localizations/src/nl-NL.ts | 2 ++ packages/localizations/src/pl-PL.ts | 2 ++ packages/localizations/src/pt-BR.ts | 2 ++ packages/localizations/src/pt-PT.ts | 2 ++ packages/localizations/src/ro-RO.ts | 2 ++ packages/localizations/src/ru-RU.ts | 2 ++ packages/localizations/src/sk-SK.ts | 2 ++ packages/localizations/src/sr-RS.ts | 2 ++ packages/localizations/src/sv-SE.ts | 2 ++ packages/localizations/src/th-TH.ts | 2 ++ packages/localizations/src/tr-TR.ts | 2 ++ packages/localizations/src/uk-UA.ts | 2 ++ packages/localizations/src/vi-VN.ts | 2 ++ packages/localizations/src/zh-CN.ts | 2 ++ packages/localizations/src/zh-TW.ts | 2 ++ packages/types/src/localization.ts | 2 ++ 43 files changed, 85 insertions(+), 3 deletions(-) diff --git a/packages/clerk-js/src/ui/components/ApiKeys/ApiKeysTable.tsx b/packages/clerk-js/src/ui/components/ApiKeys/ApiKeysTable.tsx index eead023b7a4..f07f8a6062e 100644 --- a/packages/clerk-js/src/ui/components/ApiKeys/ApiKeysTable.tsx +++ b/packages/clerk-js/src/ui/components/ApiKeys/ApiKeysTable.tsx @@ -146,7 +146,7 @@ export const ApiKeysTable = ({ @@ -97,7 +112,7 @@ export const APIKeysPage = ({ subject, perPage }: { subject: string; perPage?: n {itemCount > 5 && ( )} + setIsRevokeModalOpen(true)} + onClose={() => { + setSelectedApiKeyId(''); + setSelectedApiKeyName(''); + setIsRevokeModalOpen(false); + }} + apiKeyId={selectedApiKeyId} + apiKeyName={selectedApiKeyName} + modalRoot={revokeModalRoot} + /> ); }; diff --git a/packages/clerk-js/src/ui/components/ApiKeys/ApiKeysTable.tsx b/packages/clerk-js/src/ui/components/ApiKeys/ApiKeysTable.tsx index ce6f6464886..90c62dc4801 100644 --- a/packages/clerk-js/src/ui/components/ApiKeys/ApiKeysTable.tsx +++ b/packages/clerk-js/src/ui/components/ApiKeys/ApiKeysTable.tsx @@ -103,7 +103,7 @@ export const ApiKeysTable = ({ }: { rows: ApiKeyResource[]; isLoading: boolean; - onRevoke: (id: string) => void; + onRevoke: (id: string, name: string) => void; }) => { return (
    Name diff --git a/packages/clerk-js/src/ui/utils/createCustomPages.tsx b/packages/clerk-js/src/ui/utils/createCustomPages.tsx index 32e6eac3776..2ccca4b4cde 100644 --- a/packages/clerk-js/src/ui/utils/createCustomPages.tsx +++ b/packages/clerk-js/src/ui/utils/createCustomPages.tsx @@ -260,7 +260,7 @@ const getUserProfileDefaultRoutes = ({ commerce }: { commerce: boolean }): GetDe path: 'security', }, { - name: 'API keys', + name: localizationKeys('userProfile.navbar.apiKeys'), id: USER_PROFILE_NAVBAR_ROUTE_ID.API_KEYS, icon: InformationCircle, path: 'api-keys', @@ -306,7 +306,7 @@ const getOrganizationProfileDefaultRoutes = ({ commerce }: { commerce: boolean } path: 'organization-members', }, { - name: 'API keys', + name: localizationKeys('organizationProfile.navbar.apiKeys'), id: ORGANIZATION_PROFILE_NAVBAR_ROUTE_ID.API_KEYS, icon: InformationCircle, path: 'organization-api-keys', diff --git a/packages/localizations/src/ar-SA.ts b/packages/localizations/src/ar-SA.ts index 734d721ee0f..c32724ea0e9 100644 --- a/packages/localizations/src/ar-SA.ts +++ b/packages/localizations/src/ar-SA.ts @@ -307,6 +307,7 @@ export const arSA: LocalizationResource = { }, }, navbar: { + apiKeys: undefined, billing: undefined, description: 'إدارة منظمتك.', general: 'عام', @@ -979,6 +980,7 @@ export const arSA: LocalizationResource = { mobileButton__menu: 'القائمة', navbar: { account: 'الملف الشخصي', + apiKeys: undefined, billing: undefined, description: 'إدارة معلومات ملفك الشخصي.', security: 'حماية', diff --git a/packages/localizations/src/be-BY.ts b/packages/localizations/src/be-BY.ts index 5b9664d8167..7dcacb62430 100644 --- a/packages/localizations/src/be-BY.ts +++ b/packages/localizations/src/be-BY.ts @@ -308,6 +308,7 @@ export const beBY: LocalizationResource = { }, }, navbar: { + apiKeys: undefined, billing: undefined, description: 'Manage your organization.', general: 'General', @@ -993,6 +994,7 @@ export const beBY: LocalizationResource = { mobileButton__menu: 'Меню', navbar: { account: 'Профіль', + apiKeys: undefined, billing: undefined, description: 'Кіруйце інфармацыяй аб вашым уліковым запісе.', security: 'Бяспека', diff --git a/packages/localizations/src/bg-BG.ts b/packages/localizations/src/bg-BG.ts index 4007f38e1a0..60e1d991562 100644 --- a/packages/localizations/src/bg-BG.ts +++ b/packages/localizations/src/bg-BG.ts @@ -307,6 +307,7 @@ export const bgBG: LocalizationResource = { }, }, navbar: { + apiKeys: undefined, billing: undefined, description: 'Управление на вашата организация.', general: 'Общи', @@ -983,6 +984,7 @@ export const bgBG: LocalizationResource = { mobileButton__menu: 'Меню', navbar: { account: 'Profile', + apiKeys: undefined, billing: undefined, description: 'Управлявайте информацията в профила си.', security: 'Security', diff --git a/packages/localizations/src/ca-ES.ts b/packages/localizations/src/ca-ES.ts index ab37d787a53..cdc8dfd9fac 100644 --- a/packages/localizations/src/ca-ES.ts +++ b/packages/localizations/src/ca-ES.ts @@ -307,6 +307,7 @@ export const caES: LocalizationResource = { }, }, navbar: { + apiKeys: undefined, billing: undefined, description: 'Gestiona la teva organització.', general: 'General', @@ -989,6 +990,7 @@ export const caES: LocalizationResource = { mobileButton__menu: 'Menú', navbar: { account: 'Perfil', + apiKeys: undefined, billing: undefined, description: 'Gestiona la informació del teu compte.', security: 'Seguretat', diff --git a/packages/localizations/src/cs-CZ.ts b/packages/localizations/src/cs-CZ.ts index f7ea0a9dbdd..5f87c982c04 100644 --- a/packages/localizations/src/cs-CZ.ts +++ b/packages/localizations/src/cs-CZ.ts @@ -308,6 +308,7 @@ export const csCZ: LocalizationResource = { }, }, navbar: { + apiKeys: undefined, billing: undefined, description: 'Manage your organization.', general: 'General', @@ -982,6 +983,7 @@ export const csCZ: LocalizationResource = { mobileButton__menu: 'Menu', navbar: { account: 'Profil', + apiKeys: undefined, billing: undefined, description: 'Spravujte své údaje.', security: 'Zabezpečení', diff --git a/packages/localizations/src/da-DK.ts b/packages/localizations/src/da-DK.ts index 94aa8252dbb..a84e645ac9c 100644 --- a/packages/localizations/src/da-DK.ts +++ b/packages/localizations/src/da-DK.ts @@ -307,6 +307,7 @@ export const daDK: LocalizationResource = { }, }, navbar: { + apiKeys: undefined, billing: undefined, description: 'Administrer din organisation.', general: 'Generelt', @@ -983,6 +984,7 @@ export const daDK: LocalizationResource = { mobileButton__menu: 'Menu', navbar: { account: 'Profil', + apiKeys: undefined, billing: undefined, description: 'Administrer dine kontooplysninger.', security: 'Sikkerhed', diff --git a/packages/localizations/src/de-DE.ts b/packages/localizations/src/de-DE.ts index 7098a7b7e24..73b9d00a541 100644 --- a/packages/localizations/src/de-DE.ts +++ b/packages/localizations/src/de-DE.ts @@ -314,6 +314,7 @@ export const deDE: LocalizationResource = { }, }, navbar: { + apiKeys: undefined, billing: 'Abrechnung', description: 'Verwalten Sie ihre Organisation.', general: 'Allgemein', @@ -1009,6 +1010,7 @@ export const deDE: LocalizationResource = { mobileButton__menu: 'Menü', navbar: { account: 'Profil', + apiKeys: undefined, billing: 'Abrechnung', description: 'Verwalten Sie Ihre Kontoinformationen.', security: 'Sicherheit', diff --git a/packages/localizations/src/el-GR.ts b/packages/localizations/src/el-GR.ts index 33dff359624..7d48535f60d 100644 --- a/packages/localizations/src/el-GR.ts +++ b/packages/localizations/src/el-GR.ts @@ -308,6 +308,7 @@ export const elGR: LocalizationResource = { }, }, navbar: { + apiKeys: undefined, billing: undefined, description: 'Manage your organization.', general: 'General', @@ -993,6 +994,7 @@ export const elGR: LocalizationResource = { mobileButton__menu: 'Μενού', navbar: { account: 'Προφίλ', + apiKeys: undefined, billing: undefined, description: 'Διαχειριστείτε τις πληροφορίες του λογαριασμού σας.', security: 'Ασφάλεια', diff --git a/packages/localizations/src/en-GB.ts b/packages/localizations/src/en-GB.ts index 2246861178b..9d50fd0764f 100644 --- a/packages/localizations/src/en-GB.ts +++ b/packages/localizations/src/en-GB.ts @@ -308,6 +308,7 @@ export const enGB: LocalizationResource = { }, }, navbar: { + apiKeys: undefined, billing: undefined, description: 'Manage your organisation.', general: 'General', @@ -991,6 +992,7 @@ export const enGB: LocalizationResource = { mobileButton__menu: 'Menu', navbar: { account: 'Profile', + apiKeys: undefined, billing: undefined, description: 'Manage your account info.', security: 'Security', diff --git a/packages/localizations/src/en-US.ts b/packages/localizations/src/en-US.ts index 46377663acf..4b1938806c3 100644 --- a/packages/localizations/src/en-US.ts +++ b/packages/localizations/src/en-US.ts @@ -300,6 +300,7 @@ export const enUS: LocalizationResource = { }, }, navbar: { + apiKeys: 'API keys', billing: 'Billing', description: 'Manage your organization.', general: 'General', @@ -980,6 +981,7 @@ export const enUS: LocalizationResource = { mobileButton__menu: 'Menu', navbar: { account: 'Profile', + apiKeys: 'API keys', billing: 'Billing', description: 'Manage your account info.', security: 'Security', diff --git a/packages/localizations/src/es-ES.ts b/packages/localizations/src/es-ES.ts index 9ae11045497..c320cbb3dc2 100644 --- a/packages/localizations/src/es-ES.ts +++ b/packages/localizations/src/es-ES.ts @@ -308,6 +308,7 @@ export const esES: LocalizationResource = { }, }, navbar: { + apiKeys: undefined, billing: undefined, description: 'Gestione su organización.', general: 'General', @@ -990,6 +991,7 @@ export const esES: LocalizationResource = { mobileButton__menu: 'Menú', navbar: { account: 'Perfil', + apiKeys: undefined, billing: undefined, description: 'Gestiona la información de tu cuenta.', security: 'Seguridad', diff --git a/packages/localizations/src/es-MX.ts b/packages/localizations/src/es-MX.ts index e97a5283cd2..e6f0491066f 100644 --- a/packages/localizations/src/es-MX.ts +++ b/packages/localizations/src/es-MX.ts @@ -309,6 +309,7 @@ export const esMX: LocalizationResource = { }, }, navbar: { + apiKeys: undefined, billing: undefined, description: 'Gestiona tu organización.', general: 'General', @@ -991,6 +992,7 @@ export const esMX: LocalizationResource = { mobileButton__menu: 'Menú', navbar: { account: 'Perfil', + apiKeys: undefined, billing: undefined, description: 'Administra tu información de cuenta.', security: 'Seguridad', diff --git a/packages/localizations/src/fi-FI.ts b/packages/localizations/src/fi-FI.ts index 004428bd4b2..05405bee7ad 100644 --- a/packages/localizations/src/fi-FI.ts +++ b/packages/localizations/src/fi-FI.ts @@ -308,6 +308,7 @@ export const fiFI: LocalizationResource = { }, }, navbar: { + apiKeys: undefined, billing: undefined, description: 'Hallitse organisaatiotasi.', general: 'Yleinen', @@ -987,6 +988,7 @@ export const fiFI: LocalizationResource = { mobileButton__menu: 'Valikko', navbar: { account: 'Profiili', + apiKeys: undefined, billing: undefined, description: 'Hallitse tilisi tietoja', security: 'Turvallisuus', diff --git a/packages/localizations/src/fr-FR.ts b/packages/localizations/src/fr-FR.ts index 62314b0e3f4..4edae441652 100644 --- a/packages/localizations/src/fr-FR.ts +++ b/packages/localizations/src/fr-FR.ts @@ -309,6 +309,7 @@ export const frFR: LocalizationResource = { }, }, navbar: { + apiKeys: undefined, billing: undefined, description: 'Gérer votre organisation.', general: 'Général', @@ -992,6 +993,7 @@ export const frFR: LocalizationResource = { mobileButton__menu: 'Menu', navbar: { account: 'Compte', + apiKeys: undefined, billing: undefined, description: 'Gérer votre compte.', security: 'Sécurité', diff --git a/packages/localizations/src/he-IL.ts b/packages/localizations/src/he-IL.ts index 7c62a34172a..a26d58c68b5 100644 --- a/packages/localizations/src/he-IL.ts +++ b/packages/localizations/src/he-IL.ts @@ -305,6 +305,7 @@ export const heIL: LocalizationResource = { }, }, navbar: { + apiKeys: undefined, billing: undefined, description: 'נהל את הארגון שלך.', general: 'כללי', @@ -968,6 +969,7 @@ export const heIL: LocalizationResource = { mobileButton__menu: 'תפריט', navbar: { account: 'פרופיל', + apiKeys: undefined, billing: undefined, description: 'נהל את פרטי החשבון שלך.', security: 'אבטחה', diff --git a/packages/localizations/src/hr-HR.ts b/packages/localizations/src/hr-HR.ts index 073271cee96..e45e3be32b1 100644 --- a/packages/localizations/src/hr-HR.ts +++ b/packages/localizations/src/hr-HR.ts @@ -307,6 +307,7 @@ export const hrHR: LocalizationResource = { }, }, navbar: { + apiKeys: undefined, billing: undefined, description: 'Upravljajte svojom organizacijom.', general: 'Općenito', @@ -990,6 +991,7 @@ export const hrHR: LocalizationResource = { mobileButton__menu: 'Izbornik', navbar: { account: 'Profil', + apiKeys: undefined, billing: undefined, description: 'Upravljajte informacijama vašeg računa.', security: 'Sigurnost', diff --git a/packages/localizations/src/hu-HU.ts b/packages/localizations/src/hu-HU.ts index 2bd6a924b20..0b8954735e5 100644 --- a/packages/localizations/src/hu-HU.ts +++ b/packages/localizations/src/hu-HU.ts @@ -307,6 +307,7 @@ export const huHU: LocalizationResource = { }, }, navbar: { + apiKeys: undefined, billing: undefined, description: 'A szervezeted kezelése', general: 'Általános', @@ -988,6 +989,7 @@ export const huHU: LocalizationResource = { mobileButton__menu: 'Menü', navbar: { account: 'Profil', + apiKeys: undefined, billing: undefined, description: 'Fiók információk kezelése', security: 'Biztonság', diff --git a/packages/localizations/src/id-ID.ts b/packages/localizations/src/id-ID.ts index c0740ae31af..d9eae08dacd 100644 --- a/packages/localizations/src/id-ID.ts +++ b/packages/localizations/src/id-ID.ts @@ -308,6 +308,7 @@ export const idID: LocalizationResource = { }, }, navbar: { + apiKeys: undefined, billing: undefined, description: 'Kelola organisasi Anda.', general: 'Umum', @@ -987,6 +988,7 @@ export const idID: LocalizationResource = { mobileButton__menu: undefined, navbar: { account: 'Profil', + apiKeys: undefined, billing: undefined, description: 'Kelola info akun Anda.', security: 'Keamanan', diff --git a/packages/localizations/src/is-IS.ts b/packages/localizations/src/is-IS.ts index 5fb94f86b1e..2ca5f563fea 100644 --- a/packages/localizations/src/is-IS.ts +++ b/packages/localizations/src/is-IS.ts @@ -308,6 +308,7 @@ export const isIS: LocalizationResource = { }, }, navbar: { + apiKeys: undefined, billing: undefined, description: 'Stjórna samtökunum þínum.', general: 'Almennt', @@ -990,6 +991,7 @@ export const isIS: LocalizationResource = { mobileButton__menu: 'Valmynd', navbar: { account: 'Prófíll', + apiKeys: undefined, billing: undefined, description: 'Stjórna reikningsupplýsingum þínum.', security: 'Öryggi', diff --git a/packages/localizations/src/it-IT.ts b/packages/localizations/src/it-IT.ts index 5cc610561e1..d8d9361b033 100644 --- a/packages/localizations/src/it-IT.ts +++ b/packages/localizations/src/it-IT.ts @@ -308,6 +308,7 @@ export const itIT: LocalizationResource = { }, }, navbar: { + apiKeys: undefined, billing: undefined, description: 'Gestisci la tua organizzazione.', general: 'Generale', @@ -987,6 +988,7 @@ export const itIT: LocalizationResource = { mobileButton__menu: 'Menu', navbar: { account: 'Profilo', + apiKeys: undefined, billing: undefined, description: 'Gestisci il tuo account.', security: 'Sicurezza', diff --git a/packages/localizations/src/ja-JP.ts b/packages/localizations/src/ja-JP.ts index 8a208b6b436..f0ded225273 100644 --- a/packages/localizations/src/ja-JP.ts +++ b/packages/localizations/src/ja-JP.ts @@ -307,6 +307,7 @@ export const jaJP: LocalizationResource = { }, }, navbar: { + apiKeys: undefined, billing: undefined, description: '組織を管理します。', general: '一般', @@ -981,6 +982,7 @@ export const jaJP: LocalizationResource = { mobileButton__menu: 'メニュー', navbar: { account: 'プロファイル', + apiKeys: undefined, billing: undefined, description: 'アカウント情報管理', security: 'セキュリティ', diff --git a/packages/localizations/src/ko-KR.ts b/packages/localizations/src/ko-KR.ts index 5b4cb0b10c7..2f5ff5440cb 100644 --- a/packages/localizations/src/ko-KR.ts +++ b/packages/localizations/src/ko-KR.ts @@ -307,6 +307,7 @@ export const koKR: LocalizationResource = { }, }, navbar: { + apiKeys: undefined, billing: undefined, description: 'Manage your organization.', general: 'General', @@ -973,6 +974,7 @@ export const koKR: LocalizationResource = { mobileButton__menu: '메뉴', navbar: { account: '프로필', + apiKeys: undefined, billing: undefined, description: '계정 정보를 관리하세요.', security: '보안', diff --git a/packages/localizations/src/mn-MN.ts b/packages/localizations/src/mn-MN.ts index 4bfcd8e68ab..be8016a1787 100644 --- a/packages/localizations/src/mn-MN.ts +++ b/packages/localizations/src/mn-MN.ts @@ -308,6 +308,7 @@ export const mnMN: LocalizationResource = { }, }, navbar: { + apiKeys: undefined, billing: undefined, description: 'Байгууллагаа удирдих', general: 'Ерөнхий', @@ -986,6 +987,7 @@ export const mnMN: LocalizationResource = { mobileButton__menu: 'Цэс', navbar: { account: 'Профайл', + apiKeys: undefined, billing: undefined, description: 'Бүртгэлийнхээ мэдээллийг удирдана уу.', security: 'Аюулгүй байдал', diff --git a/packages/localizations/src/nb-NO.ts b/packages/localizations/src/nb-NO.ts index 60c3bd222eb..0ed671af368 100644 --- a/packages/localizations/src/nb-NO.ts +++ b/packages/localizations/src/nb-NO.ts @@ -307,6 +307,7 @@ export const nbNO: LocalizationResource = { }, }, navbar: { + apiKeys: undefined, billing: undefined, description: 'Administrer organisasjonen din.', general: 'Generelt', @@ -986,6 +987,7 @@ export const nbNO: LocalizationResource = { mobileButton__menu: 'Meny', navbar: { account: 'Profil', + apiKeys: undefined, billing: undefined, description: 'Administrer kontoinformasjonen din.', security: 'Sikkerhet', diff --git a/packages/localizations/src/nl-BE.ts b/packages/localizations/src/nl-BE.ts index e61911a94de..4b8a705add9 100644 --- a/packages/localizations/src/nl-BE.ts +++ b/packages/localizations/src/nl-BE.ts @@ -307,6 +307,7 @@ export const nlBE: LocalizationResource = { }, }, navbar: { + apiKeys: undefined, billing: undefined, description: 'Beheer je organisatie.', general: 'Algemeen', @@ -983,6 +984,7 @@ export const nlBE: LocalizationResource = { mobileButton__menu: 'Menu', navbar: { account: 'Profiel', + apiKeys: undefined, billing: undefined, description: 'Beheer je account informatie.', security: 'Beveiliging', diff --git a/packages/localizations/src/nl-NL.ts b/packages/localizations/src/nl-NL.ts index c0041a52e2d..5d6658b21a9 100644 --- a/packages/localizations/src/nl-NL.ts +++ b/packages/localizations/src/nl-NL.ts @@ -307,6 +307,7 @@ export const nlNL: LocalizationResource = { }, }, navbar: { + apiKeys: undefined, billing: undefined, description: 'Beheer je organisatie.', general: 'Algemeen', @@ -983,6 +984,7 @@ export const nlNL: LocalizationResource = { mobileButton__menu: 'Menu', navbar: { account: 'Profiel', + apiKeys: undefined, billing: undefined, description: 'Beheer je account informatie.', security: 'Beveiliging', diff --git a/packages/localizations/src/pl-PL.ts b/packages/localizations/src/pl-PL.ts index a0e5731460f..1a93df9cb24 100644 --- a/packages/localizations/src/pl-PL.ts +++ b/packages/localizations/src/pl-PL.ts @@ -307,6 +307,7 @@ export const plPL: LocalizationResource = { }, }, navbar: { + apiKeys: undefined, billing: undefined, description: 'Zarządzaj organizacją.', general: 'Główne', @@ -995,6 +996,7 @@ export const plPL: LocalizationResource = { mobileButton__menu: 'Menu', navbar: { account: 'Profil', + apiKeys: undefined, billing: undefined, description: 'Zarządzaj danymi konta.', security: 'Bezpieczeństwo', diff --git a/packages/localizations/src/pt-BR.ts b/packages/localizations/src/pt-BR.ts index c646c570f15..73ce6a54346 100644 --- a/packages/localizations/src/pt-BR.ts +++ b/packages/localizations/src/pt-BR.ts @@ -307,6 +307,7 @@ export const ptBR: LocalizationResource = { }, }, navbar: { + apiKeys: undefined, billing: undefined, description: 'Gerencie sua organização.', general: 'Geral', @@ -992,6 +993,7 @@ export const ptBR: LocalizationResource = { mobileButton__menu: 'Menu', navbar: { account: 'Perfil', + apiKeys: undefined, billing: undefined, description: 'Gerencie seus dados de perfil.', security: 'Segurança', diff --git a/packages/localizations/src/pt-PT.ts b/packages/localizations/src/pt-PT.ts index 9f67b36f655..b107ad98321 100644 --- a/packages/localizations/src/pt-PT.ts +++ b/packages/localizations/src/pt-PT.ts @@ -306,6 +306,7 @@ export const ptPT: LocalizationResource = { }, }, navbar: { + apiKeys: undefined, billing: undefined, description: 'Manage your organization.', general: 'General', @@ -984,6 +985,7 @@ export const ptPT: LocalizationResource = { mobileButton__menu: 'Menu', navbar: { account: 'Profile', + apiKeys: undefined, billing: undefined, description: 'Manage your account info.', security: 'Security', diff --git a/packages/localizations/src/ro-RO.ts b/packages/localizations/src/ro-RO.ts index 402a4458253..005ce0c06e0 100644 --- a/packages/localizations/src/ro-RO.ts +++ b/packages/localizations/src/ro-RO.ts @@ -309,6 +309,7 @@ export const roRO: LocalizationResource = { }, }, navbar: { + apiKeys: undefined, billing: undefined, description: 'Manage your organization.', general: 'General', @@ -992,6 +993,7 @@ export const roRO: LocalizationResource = { mobileButton__menu: 'Meniu', navbar: { account: 'Profile', + apiKeys: undefined, billing: undefined, description: 'Manage your account info.', security: 'Security', diff --git a/packages/localizations/src/ru-RU.ts b/packages/localizations/src/ru-RU.ts index 9e4dd6d6c9d..74eba2f43ab 100644 --- a/packages/localizations/src/ru-RU.ts +++ b/packages/localizations/src/ru-RU.ts @@ -311,6 +311,7 @@ export const ruRU: LocalizationResource = { }, }, navbar: { + apiKeys: undefined, billing: undefined, description: 'Управлять вашей организацией.', general: 'Общее', @@ -1001,6 +1002,7 @@ export const ruRU: LocalizationResource = { mobileButton__menu: 'Меню', navbar: { account: 'Профиль', + apiKeys: undefined, billing: undefined, description: 'Управление информацией вашей учетной записи.', security: 'Безопасность', diff --git a/packages/localizations/src/sk-SK.ts b/packages/localizations/src/sk-SK.ts index 1d87bc1990d..27212c265a8 100644 --- a/packages/localizations/src/sk-SK.ts +++ b/packages/localizations/src/sk-SK.ts @@ -307,6 +307,7 @@ export const skSK: LocalizationResource = { }, }, navbar: { + apiKeys: undefined, billing: undefined, description: 'Manage your organization.', general: 'General', @@ -982,6 +983,7 @@ export const skSK: LocalizationResource = { mobileButton__menu: 'Menu', navbar: { account: 'Profile', + apiKeys: undefined, billing: undefined, description: 'Manage your account info.', security: 'Security', diff --git a/packages/localizations/src/sr-RS.ts b/packages/localizations/src/sr-RS.ts index 0e90acd6de8..5e86e093c45 100644 --- a/packages/localizations/src/sr-RS.ts +++ b/packages/localizations/src/sr-RS.ts @@ -307,6 +307,7 @@ export const srRS: LocalizationResource = { }, }, navbar: { + apiKeys: undefined, billing: undefined, description: 'Upravljaj svojom organizacijom.', general: 'Opšte', @@ -984,6 +985,7 @@ export const srRS: LocalizationResource = { mobileButton__menu: 'Meni', navbar: { account: 'Profil', + apiKeys: undefined, billing: undefined, description: 'Upravljaj informacijama svog naloga.', security: 'Sigurnost', diff --git a/packages/localizations/src/sv-SE.ts b/packages/localizations/src/sv-SE.ts index 54eb2688ed6..bd859887dd8 100644 --- a/packages/localizations/src/sv-SE.ts +++ b/packages/localizations/src/sv-SE.ts @@ -307,6 +307,7 @@ export const svSE: LocalizationResource = { }, }, navbar: { + apiKeys: undefined, billing: undefined, description: 'Hantera din organisation.', general: 'Allmänna inställningar', @@ -987,6 +988,7 @@ export const svSE: LocalizationResource = { mobileButton__menu: 'Meny', navbar: { account: 'Profil', + apiKeys: undefined, billing: undefined, description: 'Hantera din kontoinformation.', security: 'Säkerhet', diff --git a/packages/localizations/src/th-TH.ts b/packages/localizations/src/th-TH.ts index 530c9eca779..f5c7029329f 100644 --- a/packages/localizations/src/th-TH.ts +++ b/packages/localizations/src/th-TH.ts @@ -307,6 +307,7 @@ export const thTH: LocalizationResource = { }, }, navbar: { + apiKeys: undefined, billing: undefined, description: 'จัดการองค์กรของคุณ', general: 'ทั่วไป', @@ -980,6 +981,7 @@ export const thTH: LocalizationResource = { mobileButton__menu: 'เมนู', navbar: { account: 'โปรไฟล์', + apiKeys: undefined, billing: undefined, description: 'จัดการข้อมูลบัญชีของคุณ', security: 'ความปลอดภัย', diff --git a/packages/localizations/src/tr-TR.ts b/packages/localizations/src/tr-TR.ts index 7b6659d11a6..5d3ed435ca3 100644 --- a/packages/localizations/src/tr-TR.ts +++ b/packages/localizations/src/tr-TR.ts @@ -307,6 +307,7 @@ export const trTR: LocalizationResource = { }, }, navbar: { + apiKeys: undefined, billing: undefined, description: 'Organizasyonunuzu yönetin.', general: 'Genel', @@ -988,6 +989,7 @@ export const trTR: LocalizationResource = { mobileButton__menu: 'Menü', navbar: { account: 'Profil', + apiKeys: undefined, billing: undefined, description: 'Hesap bilgilerinizi yönetin.', security: 'Güvenlik', diff --git a/packages/localizations/src/uk-UA.ts b/packages/localizations/src/uk-UA.ts index 4b8512c1364..41d6779fc9c 100644 --- a/packages/localizations/src/uk-UA.ts +++ b/packages/localizations/src/uk-UA.ts @@ -307,6 +307,7 @@ export const ukUA: LocalizationResource = { }, }, navbar: { + apiKeys: undefined, billing: undefined, description: 'Manage your organization.', general: 'General', @@ -983,6 +984,7 @@ export const ukUA: LocalizationResource = { mobileButton__menu: 'Меню', navbar: { account: 'Profile', + apiKeys: undefined, billing: undefined, description: 'Manage your account info.', security: 'Security', diff --git a/packages/localizations/src/vi-VN.ts b/packages/localizations/src/vi-VN.ts index fb43e78fb04..676862c2190 100644 --- a/packages/localizations/src/vi-VN.ts +++ b/packages/localizations/src/vi-VN.ts @@ -307,6 +307,7 @@ export const viVN: LocalizationResource = { }, }, navbar: { + apiKeys: undefined, billing: undefined, description: 'Manage your organization.', general: 'General', @@ -983,6 +984,7 @@ export const viVN: LocalizationResource = { mobileButton__menu: 'Menu', navbar: { account: 'Profile', + apiKeys: undefined, billing: undefined, description: 'Manage your account info.', security: 'Security', diff --git a/packages/localizations/src/zh-CN.ts b/packages/localizations/src/zh-CN.ts index 5e4ec7548bd..3dd68c25b67 100644 --- a/packages/localizations/src/zh-CN.ts +++ b/packages/localizations/src/zh-CN.ts @@ -303,6 +303,7 @@ export const zhCN: LocalizationResource = { }, }, navbar: { + apiKeys: undefined, billing: undefined, description: '管理您的组织。', general: '常规', @@ -959,6 +960,7 @@ export const zhCN: LocalizationResource = { mobileButton__menu: '菜单', navbar: { account: '账户', + apiKeys: undefined, billing: undefined, description: '管理您的账户。', security: '安全', diff --git a/packages/localizations/src/zh-TW.ts b/packages/localizations/src/zh-TW.ts index 479b80627ba..3c133574ee4 100644 --- a/packages/localizations/src/zh-TW.ts +++ b/packages/localizations/src/zh-TW.ts @@ -303,6 +303,7 @@ export const zhTW: LocalizationResource = { }, }, navbar: { + apiKeys: undefined, billing: undefined, description: '管理您的組織。', general: '一般的', @@ -960,6 +961,7 @@ export const zhTW: LocalizationResource = { mobileButton__menu: '菜單', navbar: { account: '輪廓', + apiKeys: undefined, billing: undefined, description: '管理您的帳戶資訊。', security: '安全', diff --git a/packages/types/src/localization.ts b/packages/types/src/localization.ts index 2b6a04f648b..d3bd73b8cf6 100644 --- a/packages/types/src/localization.ts +++ b/packages/types/src/localization.ts @@ -503,6 +503,7 @@ type _LocalizationResource = { account: LocalizationValue; security: LocalizationValue; billing: LocalizationValue; + apiKeys: LocalizationValue; }; start: { headerTitle__account: LocalizationValue; @@ -832,6 +833,7 @@ type _LocalizationResource = { general: LocalizationValue; members: LocalizationValue; billing: LocalizationValue; + apiKeys: LocalizationValue; }; badge__unverified: LocalizationValue; badge__automaticInvitation: LocalizationValue; From 6143e086ca015d010bdc4c7583461d7da9b93411 Mon Sep 17 00:00:00 2001 From: wobsoriano Date: Mon, 19 May 2025 15:06:48 -0700 Subject: [PATCH 045/116] chore: rename component to APIKeys --- packages/clerk-js/src/core/clerk.ts | 6 +++--- .../clerk-js/src/ui/components/ApiKeys/ApiKeys.tsx | 10 +++++----- .../clerk-js/src/ui/components/ApiKeys/useApiKeys.ts | 4 ++-- .../OrganizationProfile/OrganizationApiKeysPage.tsx | 8 ++++---- .../OrganizationProfile/OrganizationProfileRoutes.tsx | 6 +++--- .../src/ui/components/UserProfile/ApiKeysPage.tsx | 8 ++++---- .../ui/components/UserProfile/UserProfileRoutes.tsx | 6 +++--- .../src/ui/contexts/ClerkUIComponentsContext.tsx | 2 +- .../clerk-js/src/ui/contexts/components/ApiKeys.ts | 6 +++--- packages/clerk-js/src/ui/lazyModules/components.ts | 6 +++--- packages/clerk-js/src/ui/types.ts | 6 +++--- packages/react/src/components/index.ts | 2 +- packages/react/src/components/uiComponents.tsx | 2 +- 13 files changed, 36 insertions(+), 36 deletions(-) diff --git a/packages/clerk-js/src/core/clerk.ts b/packages/clerk-js/src/core/clerk.ts index c1dce145600..f3ad8794224 100644 --- a/packages/clerk-js/src/core/clerk.ts +++ b/packages/clerk-js/src/core/clerk.ts @@ -1045,16 +1045,16 @@ export class Clerk implements ClerkInterface { public mountApiKeys = (node: HTMLDivElement, props?: ApiKeysProps): void => { this.assertComponentsReady(this.#componentControls); - void this.#componentControls.ensureMounted({ preloadHint: 'ApiKeys' }).then(controls => + void this.#componentControls.ensureMounted({ preloadHint: 'APIKeys' }).then(controls => controls.mountComponent({ - name: 'ApiKeys', + name: 'APIKeys', appearanceKey: 'apiKeys', node, props, }), ); - this.telemetry?.record(eventPrebuiltComponentMounted('ApiKeys', props)); + this.telemetry?.record(eventPrebuiltComponentMounted('APIKeys', props)); }; public unmountApiKeys = (node: HTMLDivElement): void => { diff --git a/packages/clerk-js/src/ui/components/ApiKeys/ApiKeys.tsx b/packages/clerk-js/src/ui/components/ApiKeys/ApiKeys.tsx index 5d71f1c4980..f8756f8509a 100644 --- a/packages/clerk-js/src/ui/components/ApiKeys/ApiKeys.tsx +++ b/packages/clerk-js/src/ui/components/ApiKeys/ApiKeys.tsx @@ -13,7 +13,7 @@ import type { OnCreateParams } from './CreateApiKeyForm'; import { CreateApiKeyForm } from './CreateApiKeyForm'; import { useApiKeys } from './useApiKeys'; -export const ApiKeysInternal = ({ subject, perPage }: { subject: string; perPage?: number }) => { +export const APIKeysPage = ({ subject, perPage }: { subject: string; perPage?: number }) => { const { apiKeys, isLoading, @@ -74,7 +74,7 @@ export const ApiKeysInternal = ({ subject, perPage }: { subject: string; perPage /> diff --git a/packages/clerk-js/src/ui/components/ApiKeys/CreateApiKeyForm.tsx b/packages/clerk-js/src/ui/components/ApiKeys/CreateApiKeyForm.tsx index 99f89923750..67deee1a6bc 100644 --- a/packages/clerk-js/src/ui/components/ApiKeys/CreateApiKeyForm.tsx +++ b/packages/clerk-js/src/ui/components/ApiKeys/CreateApiKeyForm.tsx @@ -112,7 +112,6 @@ export const CreateApiKeyForm = ({ onCreate, isSubmitting }: CreateApiKeyFormPro /> setExpiration(value as Expiration)} From f520d87bcf9329f7a21933c9e5758c831a95a4f0 Mon Sep 17 00:00:00 2001 From: wobsoriano Date: Tue, 20 May 2025 14:55:58 -0700 Subject: [PATCH 053/116] chore: remove extra margin --- packages/clerk-js/src/ui/components/ApiKeys/ApiKeys.tsx | 1 - packages/clerk-js/src/ui/components/ApiKeys/ApiKeysTable.tsx | 1 - 2 files changed, 2 deletions(-) diff --git a/packages/clerk-js/src/ui/components/ApiKeys/ApiKeys.tsx b/packages/clerk-js/src/ui/components/ApiKeys/ApiKeys.tsx index f8756f8509a..9cdee759334 100644 --- a/packages/clerk-js/src/ui/components/ApiKeys/ApiKeys.tsx +++ b/packages/clerk-js/src/ui/components/ApiKeys/ApiKeys.tsx @@ -123,7 +123,6 @@ export const APIKeys = withCardStateProvider(() => { sx={[ t => ({ width: t.sizes.$220, - maxWidth: `calc(100vw - ${t.sizes.$8})`, }), ]} > diff --git a/packages/clerk-js/src/ui/components/ApiKeys/ApiKeysTable.tsx b/packages/clerk-js/src/ui/components/ApiKeys/ApiKeysTable.tsx index 5e2c7d7a4ce..ce6f6464886 100644 --- a/packages/clerk-js/src/ui/components/ApiKeys/ApiKeysTable.tsx +++ b/packages/clerk-js/src/ui/components/ApiKeys/ApiKeysTable.tsx @@ -46,7 +46,6 @@ const CopySecretButton = ({ apiKeyID }: { apiKeyID: string }) => { return (
    @@ -163,7 +163,7 @@ export const ApiKeysTable = ({ { label: localizationKeys('apiKeys.menuAction__revoke'), isDestructive: true, - onClick: () => onRevoke(apiKey.id), + onClick: () => onRevoke(apiKey.id, apiKey.name), }, ]} /> diff --git a/packages/clerk-js/src/ui/components/ApiKeys/RevokeAPIKeyConfirmationModal.tsx b/packages/clerk-js/src/ui/components/ApiKeys/RevokeAPIKeyConfirmationModal.tsx new file mode 100644 index 00000000000..e06a8ce0b78 --- /dev/null +++ b/packages/clerk-js/src/ui/components/ApiKeys/RevokeAPIKeyConfirmationModal.tsx @@ -0,0 +1,100 @@ +import { useClerk } from '@clerk/shared/react'; +import { useSWRConfig } from 'swr'; + +import { Card, Form, FormButtons, FormContainer, Modal } from '../../elements'; +import { localizationKeys } from '../../localization'; +import type { ThemableCssProp } from '../../styledSystem'; +import { useFormControl } from '../../utils'; + +type RevokeAPIKeyConfirmationModalProps = { + isOpen: boolean; + onOpen: () => void; + onClose: () => void; + apiKeyId?: string; + apiKeyName?: string; + modalRoot?: React.MutableRefObject; +}; + +export const RevokeAPIKeyConfirmationModal = ({ + isOpen, + onOpen, + onClose, + apiKeyId, + apiKeyName, + modalRoot, +}: RevokeAPIKeyConfirmationModalProps) => { + const clerk = useClerk(); + const { mutate } = useSWRConfig(); + + const handleSubmit = async (e: React.FormEvent) => { + e.preventDefault(); + if (!apiKeyId) return; + + await clerk.revokeApiKey({ apiKeyID: apiKeyId }); + void mutate({ key: 'api-keys', subject: clerk.organization?.id ?? clerk.session?.user.id }); + onClose(); + }; + + const revokeField = useFormControl('revokeConfirmation', '', { + type: 'text', + label: `Type "Revoke" to confirm`, + placeholder: 'Revoke', + isRequired: true, + }); + + // TODO: Maybe use secret key name for confirmation + const canSubmit = revokeField.value === 'Revoke'; + + if (!isOpen) { + return null; + } + + const innerDialogStyle: ThemableCssProp = { + position: 'absolute', + right: 0, + bottom: 0, + backgroundColor: 'inherit', + backdropFilter: 'blur(8px)', + display: 'flex', + justifyContent: 'center', + minHeight: '100%', + height: '100%', + width: '100%', + }; + + return ( + + + ({ + textAlign: 'left', + padding: `${t.sizes.$4} ${t.sizes.$5} ${t.sizes.$4} ${t.sizes.$6}`, + })} + > + + + + + + + + + + + + ); +}; diff --git a/packages/clerk-js/src/ui/components/OrganizationProfile/OrganizationApiKeysPage.tsx b/packages/clerk-js/src/ui/components/OrganizationProfile/OrganizationApiKeysPage.tsx index 4e939d4786f..3d816fe8858 100644 --- a/packages/clerk-js/src/ui/components/OrganizationProfile/OrganizationApiKeysPage.tsx +++ b/packages/clerk-js/src/ui/components/OrganizationProfile/OrganizationApiKeysPage.tsx @@ -2,11 +2,12 @@ import { useOrganization } from '@clerk/shared/react'; import { ApiKeysContext } from '../../contexts'; import { Col, localizationKeys } from '../../customizables'; -import { Header } from '../../elements'; +import { Header, useUnsafeNavbarContext } from '../../elements'; import { APIKeysPage } from '../ApiKeys'; export const OrganizationAPIKeysPage = () => { const { organization } = useOrganization(); + const { contentRef } = useUnsafeNavbarContext(); return ( @@ -17,7 +18,10 @@ export const OrganizationAPIKeysPage = () => { /> - + ); diff --git a/packages/clerk-js/src/ui/components/UserProfile/ApiKeysPage.tsx b/packages/clerk-js/src/ui/components/UserProfile/ApiKeysPage.tsx index 782a2943553..a2d1f1fbbf0 100644 --- a/packages/clerk-js/src/ui/components/UserProfile/ApiKeysPage.tsx +++ b/packages/clerk-js/src/ui/components/UserProfile/ApiKeysPage.tsx @@ -2,11 +2,12 @@ import { useUser } from '@clerk/shared/react'; import { ApiKeysContext } from '../../contexts'; import { Col, localizationKeys } from '../../customizables'; -import { Header } from '../../elements'; +import { Header, useUnsafeNavbarContext } from '../../elements'; import { APIKeysPage as APIKeysPageInternal } from '../ApiKeys'; export const APIKeysPage = () => { const { user } = useUser(); + const { contentRef } = useUnsafeNavbarContext(); return ( @@ -17,7 +18,10 @@ export const APIKeysPage = () => { /> - + ); diff --git a/packages/clerk-js/src/ui/components/UserProfile/UserProfileNavbar.tsx b/packages/clerk-js/src/ui/components/UserProfile/UserProfileNavbar.tsx index 995da4cb02c..29aa272e0c2 100644 --- a/packages/clerk-js/src/ui/components/UserProfile/UserProfileNavbar.tsx +++ b/packages/clerk-js/src/ui/components/UserProfile/UserProfileNavbar.tsx @@ -12,7 +12,7 @@ export const UserProfileNavbar = ( const { pages } = useUserProfileContext(); return ( - + { const AuthenticatedRoutes = withCoreUserGuard(() => { const contentRef = React.useRef(null); + return ( diff --git a/packages/clerk-js/src/ui/elements/FormContainer.tsx b/packages/clerk-js/src/ui/elements/FormContainer.tsx index 4ea0b8028f3..060023910a8 100644 --- a/packages/clerk-js/src/ui/elements/FormContainer.tsx +++ b/packages/clerk-js/src/ui/elements/FormContainer.tsx @@ -40,14 +40,16 @@ export const FormContainer = (props: PageProps) => { {headerTitle && ( )} {headerSubtitle && ( )} diff --git a/packages/clerk-js/src/ui/elements/LocalizedModal.tsx b/packages/clerk-js/src/ui/elements/LocalizedModal.tsx new file mode 100644 index 00000000000..81a1036fda0 --- /dev/null +++ b/packages/clerk-js/src/ui/elements/LocalizedModal.tsx @@ -0,0 +1,77 @@ +import { createContextAndHook } from '@clerk/shared/react'; +import { autoUpdate, flip, offset, shift, useFloating } from '@floating-ui/react'; +import React from 'react'; + +import { descriptors, Flex } from '../customizables'; +import type { ThemableCssProp } from '../styledSystem'; +import { animations } from '../styledSystem'; +import { withFloatingTree } from './contexts'; + +export const [LocalizedModalContext, _, useUnsafeLocalizedModalContext] = createContextAndHook<{ toggle?: () => void }>( + 'LocalizedModalContext', +); + +type LocalizedModalProps = React.PropsWithChildren<{ + id?: string; + handleOpen?: () => void; + handleClose?: () => void; + contentSx?: ThemableCssProp; + style?: React.CSSProperties; + anchorRef: React.RefObject; +}>; + +export const LocalizedModal = withFloatingTree((props: LocalizedModalProps) => { + const { handleClose, handleOpen, contentSx, id, style, anchorRef } = props; + const { x, y, refs, strategy } = useFloating({ + placement: 'bottom', + middleware: [offset(16), flip(), shift()], + whileElementsMounted: autoUpdate, + }); + + React.useEffect(() => { + if (anchorRef.current) { + (refs.reference as unknown as (node: HTMLElement | null) => void)(anchorRef.current); + } + }, [anchorRef, refs]); + + // Modal open/close logic (optional, can be extended) + React.useEffect(() => { + handleOpen?.(); + return () => handleClose?.(); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + + return ( + + } + aria-modal='true' + role='dialog' + style={{ + position: strategy, + top: y ?? 0, + left: x ?? 0, + zIndex: 1000, + ...style, + }} + sx={[ + t => ({ + backgroundColor: t.colors.$colorBackground, + boxShadow: t.shadows.$menuShadow, + borderRadius: t.radii.$lg, + outline: 0, + animation: `${animations.modalSlideAndFade} 180ms ${t.transitionTiming.$easeOut}`, + minWidth: '320px', + maxWidth: '100%', + padding: t.space.$6, + }), + contentSx, + ]} + > + {props.children} + + + ); +}); diff --git a/packages/clerk-js/src/ui/elements/Modal.tsx b/packages/clerk-js/src/ui/elements/Modal.tsx index 01308703099..ed98cf0aaad 100644 --- a/packages/clerk-js/src/ui/elements/Modal.tsx +++ b/packages/clerk-js/src/ui/elements/Modal.tsx @@ -19,11 +19,12 @@ type ModalProps = React.PropsWithChildren<{ containerSx?: ThemableCssProp; canCloseModal?: boolean; style?: React.CSSProperties; + portalRoot?: HTMLElement | React.MutableRefObject; }>; export const Modal = withFloatingTree((props: ModalProps) => { const { disableScrollLock, enableScrollLock } = useScrollLock(); - const { handleClose, handleOpen, contentSx, containerSx, canCloseModal, id, style } = props; + const { handleClose, handleOpen, contentSx, containerSx, canCloseModal, id, style, portalRoot } = props; const overlayRef = useRef(null); const { floating, isOpen, context, nodeId, toggle } = usePopover({ defaultOpen: true, @@ -55,6 +56,7 @@ export const Modal = withFloatingTree((props: ModalProps) => { context={context} isOpen={isOpen} outsideElementsInert + root={portalRoot} > void; close: () => void }; +type NavbarContextValue = { + isOpen: boolean; + open: () => void; + close: () => void; + contentRef?: React.RefObject; +}; export const [NavbarContext, useNavbarContext, useUnsafeNavbarContext] = createContextAndHook('NavbarContext'); -export const NavbarContextProvider = (props: React.PropsWithChildren>) => { +export const NavbarContextProvider = ({ + children, + contentRef, +}: React.PropsWithChildren<{ contentRef?: React.RefObject }>) => { const [isOpen, setIsOpen] = React.useState(false); const open = React.useCallback(() => setIsOpen(true), []); const close = React.useCallback(() => setIsOpen(false), []); - const value = React.useMemo(() => ({ value: { isOpen, open, close } }), [isOpen]); - return {props.children}; + const value = React.useMemo(() => ({ value: { isOpen, open, close, contentRef } }), [isOpen]); + return {children}; }; export type NavbarRoute = { diff --git a/packages/clerk-js/src/ui/elements/Popover.tsx b/packages/clerk-js/src/ui/elements/Popover.tsx index 52f48cd467d..826adc7860f 100644 --- a/packages/clerk-js/src/ui/elements/Popover.tsx +++ b/packages/clerk-js/src/ui/elements/Popover.tsx @@ -15,6 +15,11 @@ type PopoverProps = PropsWithChildren<{ outsideElementsInert?: boolean; order?: Array<'reference' | 'floating' | 'content'>; portal?: boolean; + /** + * The root element to render the portal into. + * @default document.body + */ + root?: HTMLElement | React.MutableRefObject; }>; export const Popover = (props: PopoverProps) => { @@ -26,13 +31,14 @@ export const Popover = (props: PopoverProps) => { nodeId, isOpen, portal = true, + root, children, } = props; if (portal) { return ( - + {isOpen && ( Date: Fri, 23 May 2025 12:38:58 -0700 Subject: [PATCH 055/116] chore: make form and table responsive --- .../src/ui/components/ApiKeys/ApiKeys.tsx | 40 +++-- .../ui/components/ApiKeys/ApiKeysTable.tsx | 166 +++++++++++------- .../components/ApiKeys/CreateApiKeyForm.tsx | 114 +++++++----- .../ApiKeys/RevokeAPIKeyConfirmationModal.tsx | 32 ++-- .../OrganizationProfileNavbar.tsx | 2 +- .../src/ui/icons/clipboard-outline.svg | 3 + packages/clerk-js/src/ui/icons/index.ts | 1 + packages/types/src/clerk.ts | 16 +- 8 files changed, 230 insertions(+), 144 deletions(-) create mode 100644 packages/clerk-js/src/ui/icons/clipboard-outline.svg diff --git a/packages/clerk-js/src/ui/components/ApiKeys/ApiKeys.tsx b/packages/clerk-js/src/ui/components/ApiKeys/ApiKeys.tsx index 9e0182935bf..e222df46f9f 100644 --- a/packages/clerk-js/src/ui/components/ApiKeys/ApiKeys.tsx +++ b/packages/clerk-js/src/ui/components/ApiKeys/ApiKeys.tsx @@ -6,9 +6,10 @@ import useSWRMutation from 'swr/mutation'; import { useApiKeysContext } from '../../contexts'; import { Box, Button, Col, Flex, Flow, Icon, localizationKeys, useLocalizations } from '../../customizables'; -import { Card, InputWithIcon, Pagination, useCardState, withCardStateProvider } from '../../elements'; +import { InputWithIcon, Pagination, useCardState, withCardStateProvider } from '../../elements'; import { Action } from '../../elements/Action'; import { MagnifyingGlass } from '../../icons'; +import { mqu } from '../../styledSystem'; import { ApiKeysTable } from './ApiKeysTable'; import type { OnCreateParams } from './CreateApiKeyForm'; import { CreateApiKeyForm } from './CreateApiKeyForm'; @@ -47,8 +48,8 @@ export const APIKeysPage = ({ subject, perPage, revokeModalRoot }: APIKeysPagePr const { t } = useLocalizations(); const clerk = useClerk(); const [isRevokeModalOpen, setIsRevokeModalOpen] = useState(false); - const [selectedApiKeyId, setSelectedApiKeyId] = useState(''); - const [selectedApiKeyName, setSelectedApiKeyName] = useState(''); + const [selectedApiKeyId, setSelectedApiKeyId] = useState(''); + const [selectedApiKeyName, setSelectedApiKeyName] = useState(''); const handleCreateApiKey = async (params: OnCreateParams, closeCardFn: () => void) => { try { @@ -71,11 +72,21 @@ export const APIKeysPage = ({ subject, perPage, revokeModalRoot }: APIKeysPagePr }; return ( - + { const { user } = useUser(); const { organization } = useOrganization(); + const subject = organization?.id ?? user?.id ?? ''; + return ( - ({ - width: t.sizes.$220, - }), - ]} - > - - - - - + ); }); diff --git a/packages/clerk-js/src/ui/components/ApiKeys/ApiKeysTable.tsx b/packages/clerk-js/src/ui/components/ApiKeys/ApiKeysTable.tsx index 90c62dc4801..1c1cc75b5ae 100644 --- a/packages/clerk-js/src/ui/components/ApiKeys/ApiKeysTable.tsx +++ b/packages/clerk-js/src/ui/components/ApiKeys/ApiKeysTable.tsx @@ -5,6 +5,7 @@ import useSWR from 'swr'; import { timeAgo } from '../../../utils/date'; import { + Box, Button, descriptors, Flex, @@ -22,8 +23,8 @@ import { } from '../../customizables'; import { ThreeDotsMenu } from '../../elements'; import { useClipboard } from '../../hooks'; -import { Check, Copy, Eye, EyeSlash } from '../../icons'; -import { common } from '../../styledSystem'; +import { Check, ClipboardOutline, Eye, EyeSlash } from '../../icons'; +import { common, mqu } from '../../styledSystem'; const useApiKeySecret = ({ apiKeyID, enabled }: { apiKeyID: string; enabled: boolean }) => { const clerk = useClerk(); @@ -50,7 +51,11 @@ const CopySecretButton = ({ apiKeyID }: { apiKeyID: string }) => { onClick={() => setEnabled(true)} focusRing={false} > - + ({ color: t.colors.$primary500 })} + /> ); }; @@ -78,7 +83,6 @@ const SecretInputWithToggle = ({ apiKeyID }: { apiKeyID: string }) => { /> ); @@ -106,73 +113,98 @@ export const ApiKeysTable = ({ onRevoke: (id: string, name: string) => void; }) => { return ( -
    - - - - - - - - - - {isLoading ? ( + ({ overflowX: 'auto', padding: t.space.$1, width: '100%' })}> +
    NameLast usedKeyActions
    ({ background: t.colors.$colorBackground })}> + - + + + + - ) : !rows.length ? ( - - ) : ( - rows.map(apiKey => ( - - - - - + + {isLoading ? ( + + - )) - )} - -
    - - NameLast usedKeyActions
    - {apiKey.name} - - Created at{' '} - {apiKey.createdAt.toLocaleDateString(undefined, { - month: 'short', - day: '2-digit', - year: 'numeric', - })} - - - {apiKey.lastUsedAt ? timeAgo(apiKey.lastUsedAt) : '-'} - - - - - - - onRevoke(apiKey.id, apiKey.name), - }, - ]} +
    +
    + ) : !rows.length ? ( + + ) : ( + rows.map(apiKey => ( +
    + + + {apiKey.name} + + + Created at{' '} + {apiKey.createdAt.toLocaleDateString(undefined, { + month: 'short', + day: '2-digit', + year: 'numeric', + })} + + + + + {apiKey.lastUsedAt ? timeAgo(apiKey.lastUsedAt) : '-'} + + + + + + + + onRevoke(apiKey.id, apiKey.name), + }, + ]} + /> +
    +
    ); }; diff --git a/packages/clerk-js/src/ui/components/ApiKeys/CreateApiKeyForm.tsx b/packages/clerk-js/src/ui/components/ApiKeys/CreateApiKeyForm.tsx index 67deee1a6bc..e0a05a2b345 100644 --- a/packages/clerk-js/src/ui/components/ApiKeys/CreateApiKeyForm.tsx +++ b/packages/clerk-js/src/ui/components/ApiKeys/CreateApiKeyForm.tsx @@ -3,6 +3,7 @@ import React, { useState } from 'react'; import { Button, Col, Flex, FormLabel, localizationKeys, Text } from '../../customizables'; import { Form, FormButtons, FormContainer, SegmentedControl } from '../../elements'; import { useActionContext } from '../../elements/Action/ActionRoot'; +import { mqu } from '../../styledSystem'; import { useFormControl } from '../../utils'; export type OnCreateParams = { name: string; description?: string; expiration: number | undefined }; @@ -103,52 +104,85 @@ export const CreateApiKeyForm = ({ onCreate, isSubmitting }: CreateApiKeyFormPro -
    ({ background: t.colors.$colorBackground })}> From d7059771b0c58f3b6dd0bf2f2f0ffe4450fb7276 Mon Sep 17 00:00:00 2001 From: wobsoriano Date: Wed, 28 May 2025 12:17:46 -0700 Subject: [PATCH 063/116] chore: prevent layout shift in date input and segmented control buttons --- .../components/ApiKeys/CreateApiKeyForm.tsx | 29 +++++++-------- .../src/ui/elements/SegmentedControl.tsx | 35 +++++++++++-------- 2 files changed, 33 insertions(+), 31 deletions(-) diff --git a/packages/clerk-js/src/ui/components/ApiKeys/CreateApiKeyForm.tsx b/packages/clerk-js/src/ui/components/ApiKeys/CreateApiKeyForm.tsx index e0a05a2b345..c50dd22295e 100644 --- a/packages/clerk-js/src/ui/components/ApiKeys/CreateApiKeyForm.tsx +++ b/packages/clerk-js/src/ui/components/ApiKeys/CreateApiKeyForm.tsx @@ -1,9 +1,9 @@ import React, { useState } from 'react'; -import { Button, Col, Flex, FormLabel, localizationKeys, Text } from '../../customizables'; +import { Box, Button, Col, Flex, FormLabel, localizationKeys, Text } from '../../customizables'; import { Form, FormButtons, FormContainer, SegmentedControl } from '../../elements'; import { useActionContext } from '../../elements/Action/ActionRoot'; -import { mqu } from '../../styledSystem'; +import { mqu, ThemableCssProp } from '../../styledSystem'; import { useFormControl } from '../../utils'; export type OnCreateParams = { name: string; description?: string; expiration: number | undefined }; @@ -90,6 +90,8 @@ export const CreateApiKeyForm = ({ onCreate, isSubmitting }: CreateApiKeyFormPro ); }; + const buttonSegmentStyle: ThemableCssProp = t => ({ height: t.sizes.$8 }); + return ( - {expiration === 'custom' && ( + {expiration === 'custom' ? ( + ) : ( + )} diff --git a/packages/clerk-js/src/ui/elements/SegmentedControl.tsx b/packages/clerk-js/src/ui/elements/SegmentedControl.tsx index ba838888d44..4b13e2a9140 100644 --- a/packages/clerk-js/src/ui/elements/SegmentedControl.tsx +++ b/packages/clerk-js/src/ui/elements/SegmentedControl.tsx @@ -3,6 +3,7 @@ import React, { createContext, useContext, useState } from 'react'; import type { LocalizationKey } from '../customizables'; import { descriptors, Flex, SimpleButton } from '../customizables'; +import type { ThemableCssProp } from '../styledSystem'; /* ------------------------------------------------------------------------------------------------- * SegmentedControl Context @@ -106,9 +107,10 @@ Root.displayName = 'SegmentedControl.Root'; interface ButtonProps { text: string | LocalizationKey; value: string; + sx?: ThemableCssProp; } -const Button = React.forwardRef(({ text, value }, ref) => { +const Button = React.forwardRef(({ text, value, sx }, ref) => { const { currentValue, onValueChange, size, fullWidth } = useSegmentedControlContext(); const isSelected = value === currentValue; @@ -126,20 +128,23 @@ const Button = React.forwardRef(({ text, value } aria-checked={isSelected} onClick={() => onValueChange(value)} isActive={isSelected} - sx={t => ({ - position: 'relative', - width: fullWidth ? '100%' : 'auto', - backgroundColor: isSelected ? t.colors.$colorBackground : 'transparent', - color: isSelected ? t.colors.$colorText : t.colors.$colorTextSecondary, - fontSize: size === 'lg' ? t.fontSizes.$md : t.fontSizes.$xs, - minHeight: t.sizes.$6, - boxShadow: isSelected ? t.shadows.$segmentedControl : 'none', - borderRadius: `calc(${t.radii.$md} - ${t.borderWidths.$normal})`, - zIndex: 1, - ':focus-visible': { - zIndex: 2, - }, - })} + sx={[ + t => ({ + position: 'relative', + width: fullWidth ? '100%' : 'auto', + backgroundColor: isSelected ? t.colors.$colorBackground : 'transparent', + color: isSelected ? t.colors.$colorText : t.colors.$colorTextSecondary, + fontSize: size === 'lg' ? t.fontSizes.$md : t.fontSizes.$xs, + minHeight: t.sizes.$6, + boxShadow: isSelected ? t.shadows.$segmentedControl : 'none', + borderRadius: `calc(${t.radii.$md} - ${t.borderWidths.$normal})`, + zIndex: 1, + ':focus-visible': { + zIndex: 2, + }, + }), + sx, + ]} /> ); }} From 0130c636b2e9347a6af3b30c3988e3a1692694dc Mon Sep 17 00:00:00 2001 From: wobsoriano Date: Wed, 28 May 2025 12:22:17 -0700 Subject: [PATCH 064/116] chore: move segmented control styling to root --- .../components/ApiKeys/CreateApiKeyForm.tsx | 9 +-- .../src/ui/elements/SegmentedControl.tsx | 55 ++++++++++--------- 2 files changed, 30 insertions(+), 34 deletions(-) diff --git a/packages/clerk-js/src/ui/components/ApiKeys/CreateApiKeyForm.tsx b/packages/clerk-js/src/ui/components/ApiKeys/CreateApiKeyForm.tsx index c50dd22295e..02d002bfebf 100644 --- a/packages/clerk-js/src/ui/components/ApiKeys/CreateApiKeyForm.tsx +++ b/packages/clerk-js/src/ui/components/ApiKeys/CreateApiKeyForm.tsx @@ -3,7 +3,7 @@ import React, { useState } from 'react'; import { Box, Button, Col, Flex, FormLabel, localizationKeys, Text } from '../../customizables'; import { Form, FormButtons, FormContainer, SegmentedControl } from '../../elements'; import { useActionContext } from '../../elements/Action/ActionRoot'; -import { mqu, ThemableCssProp } from '../../styledSystem'; +import { mqu } from '../../styledSystem'; import { useFormControl } from '../../utils'; export type OnCreateParams = { name: string; description?: string; expiration: number | undefined }; @@ -90,8 +90,6 @@ export const CreateApiKeyForm = ({ onCreate, isSubmitting }: CreateApiKeyFormPro ); }; - const buttonSegmentStyle: ThemableCssProp = t => ({ height: t.sizes.$8 }); - return ( setExpiration(value as Expiration)} fullWidth + sx={t => ({ height: t.sizes.$8 })} > diff --git a/packages/clerk-js/src/ui/elements/SegmentedControl.tsx b/packages/clerk-js/src/ui/elements/SegmentedControl.tsx index 4b13e2a9140..b80cb2054f9 100644 --- a/packages/clerk-js/src/ui/elements/SegmentedControl.tsx +++ b/packages/clerk-js/src/ui/elements/SegmentedControl.tsx @@ -41,6 +41,7 @@ interface RootProps { onChange?: (value: string) => void; size?: SegmentedControlSize; fullWidth?: boolean; + sx?: ThemableCssProp; } const Root = React.forwardRef( @@ -54,6 +55,7 @@ const Root = React.forwardRef( 'aria-labelledby': ariaLabelledby, size = 'md', fullWidth = false, + sx, }, ref, ) => { @@ -80,14 +82,17 @@ const Root = React.forwardRef( elementDescriptor={descriptors.segmentedControlRoot} aria-label={ariaLabel} aria-labelledby={ariaLabelledby} - sx={t => ({ - backgroundColor: t.colors.$neutralAlpha50, - borderRadius: t.radii.$md, - borderWidth: t.borderWidths.$normal, - borderStyle: t.borderStyles.$solid, - borderColor: t.colors.$neutralAlpha100, - isolation: 'isolate', - })} + sx={[ + t => ({ + backgroundColor: t.colors.$neutralAlpha50, + borderRadius: t.radii.$md, + borderWidth: t.borderWidths.$normal, + borderStyle: t.borderStyles.$solid, + borderColor: t.colors.$neutralAlpha100, + isolation: 'isolate', + }), + sx, + ]} /> } > @@ -107,10 +112,9 @@ Root.displayName = 'SegmentedControl.Root'; interface ButtonProps { text: string | LocalizationKey; value: string; - sx?: ThemableCssProp; } -const Button = React.forwardRef(({ text, value, sx }, ref) => { +const Button = React.forwardRef(({ text, value }, ref) => { const { currentValue, onValueChange, size, fullWidth } = useSegmentedControlContext(); const isSelected = value === currentValue; @@ -128,23 +132,20 @@ const Button = React.forwardRef(({ text, value, aria-checked={isSelected} onClick={() => onValueChange(value)} isActive={isSelected} - sx={[ - t => ({ - position: 'relative', - width: fullWidth ? '100%' : 'auto', - backgroundColor: isSelected ? t.colors.$colorBackground : 'transparent', - color: isSelected ? t.colors.$colorText : t.colors.$colorTextSecondary, - fontSize: size === 'lg' ? t.fontSizes.$md : t.fontSizes.$xs, - minHeight: t.sizes.$6, - boxShadow: isSelected ? t.shadows.$segmentedControl : 'none', - borderRadius: `calc(${t.radii.$md} - ${t.borderWidths.$normal})`, - zIndex: 1, - ':focus-visible': { - zIndex: 2, - }, - }), - sx, - ]} + sx={t => ({ + position: 'relative', + width: fullWidth ? '100%' : 'auto', + backgroundColor: isSelected ? t.colors.$colorBackground : 'transparent', + color: isSelected ? t.colors.$colorText : t.colors.$colorTextSecondary, + fontSize: size === 'lg' ? t.fontSizes.$md : t.fontSizes.$xs, + minHeight: t.sizes.$6, + boxShadow: isSelected ? t.shadows.$segmentedControl : 'none', + borderRadius: `calc(${t.radii.$md} - ${t.borderWidths.$normal})`, + zIndex: 1, + ':focus-visible': { + zIndex: 2, + }, + })} /> ); }} From 6d259293927a3e6ec97fc8b7c3c54a1f82127db2 Mon Sep 17 00:00:00 2001 From: Robert Soriano Date: Thu, 29 May 2025 17:53:44 -0700 Subject: [PATCH 065/116] chore(clerk-js,types): Conditionally render API Keys page based on enabled flag (#6032) --- .../src/core/resources/APIKeySettings.ts | 31 +++++++++++ .../src/core/resources/Environment.ts | 4 ++ .../OrganizationProfileRoutes.tsx | 22 ++++---- .../UserProfile/UserProfileRoutes.tsx | 22 ++++---- .../src/ui/utils/createCustomPages.tsx | 55 +++++++++++++------ .../clerk-js/src/utils/componentGuards.ts | 4 ++ packages/types/src/{apiKey.ts => apiKeys.ts} | 0 packages/types/src/apiKeysSettings.ts | 13 +++++ packages/types/src/clerk.ts | 8 +-- packages/types/src/environment.ts | 2 + packages/types/src/index.ts | 3 +- packages/types/src/json.ts | 2 + packages/types/src/snapshots.ts | 3 + 13 files changed, 126 insertions(+), 43 deletions(-) create mode 100644 packages/clerk-js/src/core/resources/APIKeySettings.ts rename packages/types/src/{apiKey.ts => apiKeys.ts} (100%) create mode 100644 packages/types/src/apiKeysSettings.ts diff --git a/packages/clerk-js/src/core/resources/APIKeySettings.ts b/packages/clerk-js/src/core/resources/APIKeySettings.ts new file mode 100644 index 00000000000..d21ff5fe84f --- /dev/null +++ b/packages/clerk-js/src/core/resources/APIKeySettings.ts @@ -0,0 +1,31 @@ +import type { APIKeysSettingsJSON, APIKeysSettingsJSONSnapshot, APIKeysSettingsResource } from '@clerk/types'; + +import { BaseResource } from './internal'; + +/** + * @internal + */ +export class APIKeySettings extends BaseResource implements APIKeysSettingsResource { + enabled: boolean = false; + + public constructor(data: APIKeysSettingsJSON | APIKeysSettingsJSONSnapshot | null = null) { + super(); + this.fromJSON(data); + } + + protected fromJSON(data: APIKeysSettingsJSON | APIKeysSettingsJSONSnapshot | null): this { + if (!data) { + return this; + } + + this.enabled = this.withDefault(data.enabled, false); + + return this; + } + + public __internal_toSnapshot(): APIKeysSettingsJSONSnapshot { + return { + enabled: this.enabled, + } as APIKeysSettingsJSONSnapshot; + } +} diff --git a/packages/clerk-js/src/core/resources/Environment.ts b/packages/clerk-js/src/core/resources/Environment.ts index e0bb18b426b..eadb9a9f698 100644 --- a/packages/clerk-js/src/core/resources/Environment.ts +++ b/packages/clerk-js/src/core/resources/Environment.ts @@ -10,6 +10,7 @@ import type { } from '@clerk/types'; import { eventBus, events } from '../../core/events'; +import { APIKeySettings } from './APIKeySettings'; import { AuthConfig, BaseResource, CommerceSettings, DisplayConfig, UserSettings } from './internal'; import { OrganizationSettings } from './OrganizationSettings'; @@ -23,6 +24,7 @@ export class Environment extends BaseResource implements EnvironmentResource { userSettings: UserSettingsResource = new UserSettings(); organizationSettings: OrganizationSettingsResource = new OrganizationSettings(); commerceSettings: CommerceSettingsResource = new CommerceSettings(); + apiKeysSettings: APIKeySettings = new APIKeySettings(); public static getInstance(): Environment { if (!Environment.instance) { @@ -49,6 +51,7 @@ export class Environment extends BaseResource implements EnvironmentResource { this.organizationSettings = new OrganizationSettings(data.organization_settings); this.userSettings = new UserSettings(data.user_settings); this.commerceSettings = new CommerceSettings(data.commerce_settings); + this.apiKeysSettings = new APIKeySettings(data.api_keys_settings); return this; } @@ -88,6 +91,7 @@ export class Environment extends BaseResource implements EnvironmentResource { organization_settings: this.organizationSettings.__internal_toSnapshot(), user_settings: this.userSettings.__internal_toSnapshot(), commerce_settings: this.commerceSettings.__internal_toSnapshot(), + api_keys_settings: this.apiKeysSettings.__internal_toSnapshot(), }; } } diff --git a/packages/clerk-js/src/ui/components/OrganizationProfile/OrganizationProfileRoutes.tsx b/packages/clerk-js/src/ui/components/OrganizationProfile/OrganizationProfileRoutes.tsx index 06fc475edae..da95e7d9e3d 100644 --- a/packages/clerk-js/src/ui/components/OrganizationProfile/OrganizationProfileRoutes.tsx +++ b/packages/clerk-js/src/ui/components/OrganizationProfile/OrganizationProfileRoutes.tsx @@ -24,7 +24,7 @@ const OrganizationAPIKeysPage = lazy(() => export const OrganizationProfileRoutes = () => { const { pages, isMembersPageRoot, isGeneralPageRoot, isBillingPageRoot, isApiKeysPageRoot } = useOrganizationProfileContext(); - const { commerceSettings } = useEnvironment(); + const { apiKeysSettings, commerceSettings } = useEnvironment(); const customPageRoutesWithContents = pages.contents?.map((customPage, index) => { const shouldFirstCustomItemBeOnRoot = !isGeneralPageRoot && !isMembersPageRoot && index === 0; @@ -96,15 +96,17 @@ export const OrganizationProfileRoutes = () => { )} - - - - - - - - - + {apiKeysSettings.enabled && ( + + + + + + + + + + )} ); diff --git a/packages/clerk-js/src/ui/components/UserProfile/UserProfileRoutes.tsx b/packages/clerk-js/src/ui/components/UserProfile/UserProfileRoutes.tsx index 33b9c6ea15e..9ec65b6a3a4 100644 --- a/packages/clerk-js/src/ui/components/UserProfile/UserProfileRoutes.tsx +++ b/packages/clerk-js/src/ui/components/UserProfile/UserProfileRoutes.tsx @@ -23,7 +23,7 @@ const APIKeysPage = lazy(() => export const UserProfileRoutes = () => { const { pages } = useUserProfileContext(); - const { commerceSettings } = useEnvironment(); + const { apiKeysSettings, commerceSettings } = useEnvironment(); const isAccountPageRoot = pages.routes[0].id === USER_PROFILE_NAVBAR_ROUTE_ID.ACCOUNT; const isSecurityPageRoot = pages.routes[0].id === USER_PROFILE_NAVBAR_ROUTE_ID.SECURITY; @@ -87,15 +87,17 @@ export const UserProfileRoutes = () => { )} - - - - - - - - - + {apiKeysSettings.enabled && ( + + + + + + + + + + )} ); diff --git a/packages/clerk-js/src/ui/utils/createCustomPages.tsx b/packages/clerk-js/src/ui/utils/createCustomPages.tsx index 315eb8ed48f..c3929f842c6 100644 --- a/packages/clerk-js/src/ui/utils/createCustomPages.tsx +++ b/packages/clerk-js/src/ui/utils/createCustomPages.tsx @@ -1,6 +1,12 @@ import type { CustomPage, EnvironmentResource, LoadedClerk } from '@clerk/types'; -import { disabledBillingFeature, hasPaidOrgPlans, hasPaidUserPlans, isValidUrl } from '../../utils'; +import { + disabledAPIKeysFeature, + disabledBillingFeature, + hasPaidOrgPlans, + hasPaidUserPlans, + isValidUrl, +} from '../../utils'; import { ORGANIZATION_PROFILE_NAVBAR_ROUTE_ID, USER_PROFILE_NAVBAR_ROUTE_ID } from '../constants'; import type { NavbarRoute } from '../elements/Navbar'; import { Code, CreditCard, Organization, TickShield, User, Users } from '../icons'; @@ -43,7 +49,7 @@ type GetDefaultRoutesReturnType = { type CreateCustomPagesParams = { customPages: CustomPage[]; - getDefaultRoutes: ({ commerce }: { commerce: boolean }) => GetDefaultRoutesReturnType; + getDefaultRoutes: ({ commerce, apiKeys }: { commerce: boolean; apiKeys: boolean }) => GetDefaultRoutesReturnType; setFirstPathToRoot: (routes: NavbarRoute[]) => NavbarRoute[]; excludedPathsFromDuplicateWarning: string[]; }; @@ -93,6 +99,7 @@ const createCustomPages = ( commerce: !disabledBillingFeature(clerk, environment) && (organization ? hasPaidOrgPlans(clerk, environment) : hasPaidUserPlans(clerk, environment)), + apiKeys: !disabledAPIKeysFeature(clerk, environment), }); if (isDevelopmentSDK(clerk)) { @@ -245,7 +252,13 @@ const assertExternalLinkAsRoot = (routes: NavbarRoute[]) => { } }; -const getUserProfileDefaultRoutes = ({ commerce }: { commerce: boolean }): GetDefaultRoutesReturnType => { +const getUserProfileDefaultRoutes = ({ + commerce, + apiKeys, +}: { + commerce: boolean; + apiKeys: boolean; +}): GetDefaultRoutesReturnType => { const INITIAL_ROUTES: NavbarRoute[] = [ { name: localizationKeys('userProfile.navbar.account'), @@ -259,12 +272,6 @@ const getUserProfileDefaultRoutes = ({ commerce }: { commerce: boolean }): GetDe icon: TickShield, path: 'security', }, - { - name: localizationKeys('userProfile.navbar.apiKeys'), - id: USER_PROFILE_NAVBAR_ROUTE_ID.API_KEYS, - icon: Code, - path: 'api-keys', - }, ]; if (commerce) { INITIAL_ROUTES.push({ @@ -274,6 +281,14 @@ const getUserProfileDefaultRoutes = ({ commerce }: { commerce: boolean }): GetDe path: 'billing', }); } + if (apiKeys) { + INITIAL_ROUTES.push({ + name: localizationKeys('userProfile.navbar.apiKeys'), + id: USER_PROFILE_NAVBAR_ROUTE_ID.API_KEYS, + icon: Code, + path: 'api-keys', + }); + } const pageToRootNavbarRouteMap: Record = { profile: INITIAL_ROUTES.find(r => r.id === USER_PROFILE_NAVBAR_ROUTE_ID.ACCOUNT) as NavbarRoute, @@ -291,7 +306,13 @@ const getUserProfileDefaultRoutes = ({ commerce }: { commerce: boolean }): GetDe return { INITIAL_ROUTES, pageToRootNavbarRouteMap, validReorderItemLabels }; }; -const getOrganizationProfileDefaultRoutes = ({ commerce }: { commerce: boolean }): GetDefaultRoutesReturnType => { +const getOrganizationProfileDefaultRoutes = ({ + commerce, + apiKeys, +}: { + commerce: boolean; + apiKeys: boolean; +}): GetDefaultRoutesReturnType => { const INITIAL_ROUTES: NavbarRoute[] = [ { name: localizationKeys('organizationProfile.navbar.general'), @@ -305,12 +326,6 @@ const getOrganizationProfileDefaultRoutes = ({ commerce }: { commerce: boolean } icon: Users, path: 'organization-members', }, - { - name: localizationKeys('organizationProfile.navbar.apiKeys'), - id: ORGANIZATION_PROFILE_NAVBAR_ROUTE_ID.API_KEYS, - icon: Code, - path: 'organization-api-keys', - }, ]; if (commerce) { INITIAL_ROUTES.push({ @@ -320,6 +335,14 @@ const getOrganizationProfileDefaultRoutes = ({ commerce }: { commerce: boolean } path: 'organization-billing', }); } + if (apiKeys) { + INITIAL_ROUTES.push({ + name: localizationKeys('organizationProfile.navbar.apiKeys'), + id: ORGANIZATION_PROFILE_NAVBAR_ROUTE_ID.API_KEYS, + icon: Code, + path: 'organization-api-keys', + }); + } const pageToRootNavbarRouteMap: Record = { 'invite-members': INITIAL_ROUTES.find(r => r.id === ORGANIZATION_PROFILE_NAVBAR_ROUTE_ID.MEMBERS) as NavbarRoute, diff --git a/packages/clerk-js/src/utils/componentGuards.ts b/packages/clerk-js/src/utils/componentGuards.ts index 1444da564a8..78161d27347 100644 --- a/packages/clerk-js/src/utils/componentGuards.ts +++ b/packages/clerk-js/src/utils/componentGuards.ts @@ -33,3 +33,7 @@ export const hasPaidOrgPlans: ComponentGuard = (_, environment) => { export const hasPaidUserPlans: ComponentGuard = (_, environment) => { return environment?.commerceSettings.billing.hasPaidUserPlans || false; }; + +export const disabledAPIKeysFeature: ComponentGuard = (_, environment) => { + return !environment?.apiKeysSettings.enabled; +}; diff --git a/packages/types/src/apiKey.ts b/packages/types/src/apiKeys.ts similarity index 100% rename from packages/types/src/apiKey.ts rename to packages/types/src/apiKeys.ts diff --git a/packages/types/src/apiKeysSettings.ts b/packages/types/src/apiKeysSettings.ts new file mode 100644 index 00000000000..f5871c1ed2d --- /dev/null +++ b/packages/types/src/apiKeysSettings.ts @@ -0,0 +1,13 @@ +import type { ClerkResourceJSON } from './json'; +import type { ClerkResource } from './resource'; +import type { APIKeysSettingsJSONSnapshot } from './snapshots'; + +export interface APIKeysSettingsJSON extends ClerkResourceJSON { + enabled: boolean; +} + +export interface APIKeysSettingsResource extends ClerkResource { + enabled: boolean; + + __internal_toSnapshot: () => APIKeysSettingsJSONSnapshot; +} diff --git a/packages/types/src/clerk.ts b/packages/types/src/clerk.ts index efa7c4ff589..d211ba92401 100644 --- a/packages/types/src/clerk.ts +++ b/packages/types/src/clerk.ts @@ -1,4 +1,4 @@ -import type { APIKeyResource } from './apiKey'; +import type { APIKeyResource } from './apiKeys'; import type { APIKeysTheme, Appearance, @@ -762,15 +762,13 @@ export interface Clerk { __internal_setActiveInProgress: boolean; /** - * @experimental - * EARLY ACCESS: This API is in early access and may change in future releases. + * This API is in early access and may change in future releases. * * Retrieves all API keys for the current user or organization. */ getApiKeys: (params?: GetAPIKeysParams) => Promise; /** - * @experimental * This API is in early access and may change in future releases. * * Retrieves the secret for a given API key ID. @@ -778,7 +776,6 @@ export interface Clerk { getApiKeySecret: (apiKeyID: string) => Promise; /** - * @experimental * This API is in early access and may change in future releases. * * Creates a new API key. @@ -786,7 +783,6 @@ export interface Clerk { createApiKey: (params: CreateAPIKeyParams) => Promise; /** - * @experimental * This API is in early access and may change in future releases. * * Revokes a given API key by ID. diff --git a/packages/types/src/environment.ts b/packages/types/src/environment.ts index 91076d2b119..b8e2d23cbc2 100644 --- a/packages/types/src/environment.ts +++ b/packages/types/src/environment.ts @@ -1,3 +1,4 @@ +import type { APIKeysSettingsResource } from './apiKeysSettings'; import type { AuthConfigResource } from './authConfig'; import type { CommerceSettingsResource } from './commerceSettings'; import type { DisplayConfigResource } from './displayConfig'; @@ -12,6 +13,7 @@ export interface EnvironmentResource extends ClerkResource { authConfig: AuthConfigResource; displayConfig: DisplayConfigResource; commerceSettings: CommerceSettingsResource; + apiKeysSettings: APIKeysSettingsResource; isSingleSession: () => boolean; isProduction: () => boolean; isDevelopmentOrStaging: () => boolean; diff --git a/packages/types/src/index.ts b/packages/types/src/index.ts index 8586d5b2ba1..7db0228bbe8 100644 --- a/packages/types/src/index.ts +++ b/packages/types/src/index.ts @@ -66,7 +66,8 @@ export * from './passkey'; export * from './customMenuItems'; export * from './samlConnection'; export * from './waitlist'; -export * from './apiKey'; +export * from './apiKeys'; +export * from './apiKeysSettings'; export * from './snapshots'; export * from './authObject'; export * from './phoneCodeChannel'; diff --git a/packages/types/src/json.ts b/packages/types/src/json.ts index cde1985d782..5bea70c611a 100644 --- a/packages/types/src/json.ts +++ b/packages/types/src/json.ts @@ -2,6 +2,7 @@ * Currently representing API DTOs in their JSON form. */ +import type { APIKeysSettingsJSON } from './apiKeysSettings'; import type { CommercePaymentChargeType, CommercePaymentSourceStatus, @@ -70,6 +71,7 @@ export interface ImageJSON { export interface EnvironmentJSON extends ClerkResourceJSON { auth_config: AuthConfigJSON; + api_keys_settings: APIKeysSettingsJSON; commerce_settings: CommerceSettingsJSON; display_config: DisplayConfigJSON; user_settings: UserSettingsJSON; diff --git a/packages/types/src/snapshots.ts b/packages/types/src/snapshots.ts index 69637433f06..99b53912eea 100644 --- a/packages/types/src/snapshots.ts +++ b/packages/types/src/snapshots.ts @@ -1,5 +1,6 @@ // this file contains the types returned by the __internal_toSnapshot method of the resources +import type { APIKeysSettingsJSON } from './apiKeysSettings'; import type { CommerceSettingsJSON } from './commerceSettings'; import type { DisplayConfigJSON } from './displayConfig'; import type { @@ -191,3 +192,5 @@ export type CommerceSettingsJSONSnapshot = CommerceSettingsJSON; export type CommercePlanJSONSnapshot = CommercePlanJSON; export type CommerceFeatureJSONSnapshot = CommerceFeatureJSON; + +export type APIKeysSettingsJSONSnapshot = APIKeysSettingsJSON; From 163ff718a455153ff4c7b4ecb1b06f8776b0f6e0 Mon Sep 17 00:00:00 2001 From: wobsoriano Date: Thu, 29 May 2025 19:09:33 -0700 Subject: [PATCH 066/116] chore: remove unused component --- .../src/ui/elements/LocalizedModal.tsx | 77 ------------------- 1 file changed, 77 deletions(-) delete mode 100644 packages/clerk-js/src/ui/elements/LocalizedModal.tsx diff --git a/packages/clerk-js/src/ui/elements/LocalizedModal.tsx b/packages/clerk-js/src/ui/elements/LocalizedModal.tsx deleted file mode 100644 index 81a1036fda0..00000000000 --- a/packages/clerk-js/src/ui/elements/LocalizedModal.tsx +++ /dev/null @@ -1,77 +0,0 @@ -import { createContextAndHook } from '@clerk/shared/react'; -import { autoUpdate, flip, offset, shift, useFloating } from '@floating-ui/react'; -import React from 'react'; - -import { descriptors, Flex } from '../customizables'; -import type { ThemableCssProp } from '../styledSystem'; -import { animations } from '../styledSystem'; -import { withFloatingTree } from './contexts'; - -export const [LocalizedModalContext, _, useUnsafeLocalizedModalContext] = createContextAndHook<{ toggle?: () => void }>( - 'LocalizedModalContext', -); - -type LocalizedModalProps = React.PropsWithChildren<{ - id?: string; - handleOpen?: () => void; - handleClose?: () => void; - contentSx?: ThemableCssProp; - style?: React.CSSProperties; - anchorRef: React.RefObject; -}>; - -export const LocalizedModal = withFloatingTree((props: LocalizedModalProps) => { - const { handleClose, handleOpen, contentSx, id, style, anchorRef } = props; - const { x, y, refs, strategy } = useFloating({ - placement: 'bottom', - middleware: [offset(16), flip(), shift()], - whileElementsMounted: autoUpdate, - }); - - React.useEffect(() => { - if (anchorRef.current) { - (refs.reference as unknown as (node: HTMLElement | null) => void)(anchorRef.current); - } - }, [anchorRef, refs]); - - // Modal open/close logic (optional, can be extended) - React.useEffect(() => { - handleOpen?.(); - return () => handleClose?.(); - // eslint-disable-next-line react-hooks/exhaustive-deps - }, []); - - return ( - - } - aria-modal='true' - role='dialog' - style={{ - position: strategy, - top: y ?? 0, - left: x ?? 0, - zIndex: 1000, - ...style, - }} - sx={[ - t => ({ - backgroundColor: t.colors.$colorBackground, - boxShadow: t.shadows.$menuShadow, - borderRadius: t.radii.$lg, - outline: 0, - animation: `${animations.modalSlideAndFade} 180ms ${t.transitionTiming.$easeOut}`, - minWidth: '320px', - maxWidth: '100%', - padding: t.space.$6, - }), - contentSx, - ]} - > - {props.children} - - - ); -}); From 2e38987763fe4716e4449bb142651d72b8f44356 Mon Sep 17 00:00:00 2001 From: wobsoriano Date: Fri, 30 May 2025 10:51:56 -0700 Subject: [PATCH 067/116] fix types --- packages/react/src/isomorphicClerk.ts | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/packages/react/src/isomorphicClerk.ts b/packages/react/src/isomorphicClerk.ts index b3a7b2db84f..6e46f724a03 100644 --- a/packages/react/src/isomorphicClerk.ts +++ b/packages/react/src/isomorphicClerk.ts @@ -7,8 +7,8 @@ import type { __internal_PlanDetailsProps, __internal_UserVerificationModalProps, __internal_UserVerificationProps, - ApiKeyResource, - ApiKeysProps, + APIKeyResource, + APIKeysProps, AuthenticateWithCoinbaseWalletParams, AuthenticateWithGoogleOneTapParams, AuthenticateWithMetamaskParams, @@ -19,11 +19,11 @@ import type { ClerkStatus, ClientResource, CommerceBillingNamespace, - CreateApiKeyParams, + CreateAPIKeyParams, CreateOrganizationParams, CreateOrganizationProps, DomainOrProxyUrl, - GetApiKeysParams, + GetAPIKeysParams, GoogleOneTapProps, HandleEmailLinkVerificationParams, HandleOAuthCallbackParams, @@ -136,7 +136,7 @@ export class IsomorphicClerk implements IsomorphicLoadedClerk { private premountMethodCalls = new Map, MethodCallback>(); private premountWaitlistNodes = new Map(); private premountPricingTableNodes = new Map(); - private premountApiKeysNodes = new Map(); + private premountApiKeysNodes = new Map(); // A separate Map of `addListener` method calls to handle multiple listeners. private premountAddListenerCalls = new Map< ListenerCallback, @@ -1060,7 +1060,7 @@ export class IsomorphicClerk implements IsomorphicLoadedClerk { } }; - mountApiKeys = (node: HTMLDivElement, props?: ApiKeysProps): void => { + mountApiKeys = (node: HTMLDivElement, props?: APIKeysProps): void => { if (this.clerkjs && this.loaded) { this.clerkjs.mountApiKeys(node, props); } else { @@ -1312,10 +1312,10 @@ export class IsomorphicClerk implements IsomorphicLoadedClerk { } }; - getApiKeys = async (params?: GetApiKeysParams): Promise => { + getApiKeys = async (params?: GetAPIKeysParams): Promise => { const callback = () => this.clerkjs?.getApiKeys(params); if (this.clerkjs && this.loaded) { - return callback() as Promise; + return callback() as Promise; } else { this.premountMethodCalls.set('getApiKeys', callback); } @@ -1330,19 +1330,19 @@ export class IsomorphicClerk implements IsomorphicLoadedClerk { } }; - createApiKey = async (params: CreateApiKeyParams): Promise => { + createApiKey = async (params: CreateAPIKeyParams): Promise => { const callback = () => this.clerkjs?.createApiKey(params); if (this.clerkjs && this.loaded) { - return callback() as Promise; + return callback() as Promise; } else { this.premountMethodCalls.set('createApiKey', callback); } }; - revokeApiKey = async (params: RevokeApiKeyParams): Promise => { + revokeApiKey = async (params: RevokeApiKeyParams): Promise => { const callback = () => this.clerkjs?.revokeApiKey(params); if (this.clerkjs && this.loaded) { - return callback() as Promise; + return callback() as Promise; } else { this.premountMethodCalls.set('revokeApiKey', callback); } From 3cbf8768a6b9febc49a41a14c96657855af4215d Mon Sep 17 00:00:00 2001 From: wobsoriano Date: Fri, 30 May 2025 10:52:21 -0700 Subject: [PATCH 068/116] fix types --- packages/react/src/isomorphicClerk.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/react/src/isomorphicClerk.ts b/packages/react/src/isomorphicClerk.ts index 6e46f724a03..414b6fd3197 100644 --- a/packages/react/src/isomorphicClerk.ts +++ b/packages/react/src/isomorphicClerk.ts @@ -37,7 +37,7 @@ import type { OrganizationSwitcherProps, PricingTableProps, RedirectOptions, - RevokeApiKeyParams, + RevokeAPIKeyParams, SetActiveParams, SignInProps, SignInRedirectOptions, @@ -1339,7 +1339,7 @@ export class IsomorphicClerk implements IsomorphicLoadedClerk { } }; - revokeApiKey = async (params: RevokeApiKeyParams): Promise => { + revokeApiKey = async (params: RevokeAPIKeyParams): Promise => { const callback = () => this.clerkjs?.revokeApiKey(params); if (this.clerkjs && this.loaded) { return callback() as Promise; From 398cbfbcb4d9a34119f898f00ef932ca3c086497 Mon Sep 17 00:00:00 2001 From: wobsoriano Date: Fri, 30 May 2025 10:53:46 -0700 Subject: [PATCH 069/116] chore: update changesets --- .changeset/fluffy-numbers-stick.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.changeset/fluffy-numbers-stick.md b/.changeset/fluffy-numbers-stick.md index a81ea8cc22f..8ae9eb674ab 100644 --- a/.changeset/fluffy-numbers-stick.md +++ b/.changeset/fluffy-numbers-stick.md @@ -1,6 +1,6 @@ --- -'@clerk/localizations': major -'@clerk/types': major +'@clerk/localizations': patch +'@clerk/types': patch --- Introduce types and locales for `` component From 26c11b83884b723dd2f3c17279f81f45fc8f8a26 Mon Sep 17 00:00:00 2001 From: wobsoriano Date: Fri, 30 May 2025 11:05:58 -0700 Subject: [PATCH 070/116] chore: fix snapshot tests --- .../__tests__/__snapshots__/Environment.test.ts.snap | 11 +++++++++++ .../src/__tests__/__snapshots__/exports.test.ts.snap | 1 + 2 files changed, 12 insertions(+) diff --git a/packages/clerk-js/src/core/resources/__tests__/__snapshots__/Environment.test.ts.snap b/packages/clerk-js/src/core/resources/__tests__/__snapshots__/Environment.test.ts.snap index 7677432435f..823f477df11 100644 --- a/packages/clerk-js/src/core/resources/__tests__/__snapshots__/Environment.test.ts.snap +++ b/packages/clerk-js/src/core/resources/__tests__/__snapshots__/Environment.test.ts.snap @@ -2,6 +2,9 @@ exports[`Environment __internal_toSnapshot() 1`] = ` { + "api_keys_settings": { + "enabled": false, + }, "auth_config": { "claimed_at": null, "id": "", @@ -275,6 +278,10 @@ exports[`Environment __internal_toSnapshot() 1`] = ` exports[`Environment defaults values when instantiated without arguments 1`] = ` Environment { + "apiKeysSettings": APIKeySettings { + "enabled": false, + "pathRoot": "", + }, "authConfig": AuthConfig { "claimedAt": null, "pathRoot": "", @@ -507,6 +514,10 @@ Environment { exports[`Environment has the same initial properties 1`] = ` Environment { + "apiKeysSettings": APIKeySettings { + "enabled": false, + "pathRoot": "", + }, "authConfig": AuthConfig { "claimedAt": null, "pathRoot": "", diff --git a/packages/tanstack-react-start/src/__tests__/__snapshots__/exports.test.ts.snap b/packages/tanstack-react-start/src/__tests__/__snapshots__/exports.test.ts.snap index f0247688460..296c327d52e 100644 --- a/packages/tanstack-react-start/src/__tests__/__snapshots__/exports.test.ts.snap +++ b/packages/tanstack-react-start/src/__tests__/__snapshots__/exports.test.ts.snap @@ -14,6 +14,7 @@ exports[`errors public exports > should not change unexpectedly 1`] = ` exports[`root public exports > should not change unexpectedly 1`] = ` [ + "APIKeys", "AuthenticateWithRedirectCallback", "ClerkDegraded", "ClerkFailed", From 518dbbb57a1e00cdb916551cdc2928236ec3f2a6 Mon Sep 17 00:00:00 2001 From: wobsoriano Date: Fri, 30 May 2025 11:11:37 -0700 Subject: [PATCH 071/116] chore: fix react-router snapshot tests --- .../src/__tests__/__snapshots__/exports.test.ts.snap | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/react-router/src/__tests__/__snapshots__/exports.test.ts.snap b/packages/react-router/src/__tests__/__snapshots__/exports.test.ts.snap index 49c10a61589..3a757e9ec5d 100644 --- a/packages/react-router/src/__tests__/__snapshots__/exports.test.ts.snap +++ b/packages/react-router/src/__tests__/__snapshots__/exports.test.ts.snap @@ -2,6 +2,7 @@ exports[`root public exports > should not change unexpectedly 1`] = ` [ + "APIKeys", "AuthenticateWithRedirectCallback", "ClerkDegraded", "ClerkFailed", From 4aea4306f7644716121cd29056f3c07f8050d3d5 Mon Sep 17 00:00:00 2001 From: wobsoriano Date: Fri, 30 May 2025 11:43:09 -0700 Subject: [PATCH 072/116] chore: fix chrome-extension snapshot tests --- .../src/__tests__/__snapshots__/exports.test.ts.snap | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/chrome-extension/src/__tests__/__snapshots__/exports.test.ts.snap b/packages/chrome-extension/src/__tests__/__snapshots__/exports.test.ts.snap index f3220be64f2..209abfc6d51 100644 --- a/packages/chrome-extension/src/__tests__/__snapshots__/exports.test.ts.snap +++ b/packages/chrome-extension/src/__tests__/__snapshots__/exports.test.ts.snap @@ -2,6 +2,7 @@ exports[`public exports should not include a breaking change 1`] = ` [ + "APIKeys", "AuthenticateWithRedirectCallback", "ClerkDegraded", "ClerkFailed", From aa0adf661398718d4b7c9ccfb445a6fd451f37a7 Mon Sep 17 00:00:00 2001 From: wobsoriano Date: Fri, 30 May 2025 12:23:20 -0700 Subject: [PATCH 073/116] chore: fix typedoc tests --- .typedoc/__tests__/__snapshots__/file-structure.test.ts.snap | 1 + 1 file changed, 1 insertion(+) diff --git a/.typedoc/__tests__/__snapshots__/file-structure.test.ts.snap b/.typedoc/__tests__/__snapshots__/file-structure.test.ts.snap index ff68a3e6cdb..6c52ea5651d 100644 --- a/.typedoc/__tests__/__snapshots__/file-structure.test.ts.snap +++ b/.typedoc/__tests__/__snapshots__/file-structure.test.ts.snap @@ -125,6 +125,7 @@ exports[`Typedoc output > should have a deliberate file structure 1`] = ` "nextjs/create-sync-get-auth.mdx", "nextjs/current-user.mdx", "nextjs/get-auth.mdx", + "clerk-react/api-keys.mdx", "clerk-react/clerk-provider-props.mdx", "clerk-react/protect.mdx", "clerk-react/redirect-to-create-organization.mdx", From c23d4b0f307034eec7b10d98afa65c869d8fccb0 Mon Sep 17 00:00:00 2001 From: Robert Soriano Date: Mon, 2 Jun 2025 16:30:23 -0700 Subject: [PATCH 074/116] Update ApiKeysTable.tsx Co-authored-by: panteliselef --- packages/clerk-js/src/ui/components/ApiKeys/ApiKeysTable.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/clerk-js/src/ui/components/ApiKeys/ApiKeysTable.tsx b/packages/clerk-js/src/ui/components/ApiKeys/ApiKeysTable.tsx index 254c5672620..5e25d16a137 100644 --- a/packages/clerk-js/src/ui/components/ApiKeys/ApiKeysTable.tsx +++ b/packages/clerk-js/src/ui/components/ApiKeys/ApiKeysTable.tsx @@ -74,7 +74,7 @@ const SecretInputWithToggle = ({ apiKeyID }: { apiKeyID: string }) => { > ({ From 8d09a9f8ced963084019432fdde9c99147765775 Mon Sep 17 00:00:00 2001 From: Robert Soriano Date: Tue, 3 Jun 2025 08:06:49 -0700 Subject: [PATCH 075/116] Update packages/localizations/src/de-DE.ts Co-authored-by: Lennart --- packages/localizations/src/de-DE.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/localizations/src/de-DE.ts b/packages/localizations/src/de-DE.ts index 67d1b67dfb8..05266cf84da 100644 --- a/packages/localizations/src/de-DE.ts +++ b/packages/localizations/src/de-DE.ts @@ -851,7 +851,7 @@ export const deDE: LocalizationResource = { }, userProfile: { apiKeysPage: { - title: undefined, + title: 'API-Keys', }, backupCodePage: { actionLabel__copied: 'Kopiert!', From 6857961fcd92856fdd74e357c1aeed5526acb7f0 Mon Sep 17 00:00:00 2001 From: Robert Soriano Date: Tue, 3 Jun 2025 08:06:56 -0700 Subject: [PATCH 076/116] Update packages/localizations/src/de-DE.ts Co-authored-by: Lennart --- packages/localizations/src/de-DE.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/localizations/src/de-DE.ts b/packages/localizations/src/de-DE.ts index 05266cf84da..a4ce5703155 100644 --- a/packages/localizations/src/de-DE.ts +++ b/packages/localizations/src/de-DE.ts @@ -1015,7 +1015,7 @@ export const deDE: LocalizationResource = { mobileButton__menu: 'Menü', navbar: { account: 'Profil', - apiKeys: undefined, + apiKeys: 'API-Keys', billing: 'Abrechnung', description: 'Verwalten Sie Ihre Kontoinformationen.', security: 'Sicherheit', From 3380f9a2acab52f0c63d275f22b15a109dfecbb9 Mon Sep 17 00:00:00 2001 From: Robert Soriano Date: Tue, 3 Jun 2025 08:07:04 -0700 Subject: [PATCH 077/116] Update packages/localizations/src/de-DE.ts Co-authored-by: Lennart --- packages/localizations/src/de-DE.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/localizations/src/de-DE.ts b/packages/localizations/src/de-DE.ts index a4ce5703155..d37442dd94d 100644 --- a/packages/localizations/src/de-DE.ts +++ b/packages/localizations/src/de-DE.ts @@ -319,7 +319,7 @@ export const deDE: LocalizationResource = { }, }, navbar: { - apiKeys: undefined, + apiKeys: 'API-Keys', billing: 'Abrechnung', description: 'Verwalten Sie ihre Organisation.', general: 'Allgemein', From bdef2fe6c1033632b6b075f53985ec7c22149cdd Mon Sep 17 00:00:00 2001 From: Robert Soriano Date: Tue, 3 Jun 2025 08:07:25 -0700 Subject: [PATCH 078/116] Update packages/localizations/src/de-DE.ts Co-authored-by: Lennart --- packages/localizations/src/de-DE.ts | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/packages/localizations/src/de-DE.ts b/packages/localizations/src/de-DE.ts index d37442dd94d..8c49f14a854 100644 --- a/packages/localizations/src/de-DE.ts +++ b/packages/localizations/src/de-DE.ts @@ -15,17 +15,17 @@ import type { LocalizationResource } from '@clerk/types'; export const deDE: LocalizationResource = { locale: 'de-DE', apiKeys: { - action__add: undefined, - action__search: undefined, - detailsTitle__emptyRow: undefined, - formButtonPrimary__add: undefined, - formHint: undefined, - formTitle: undefined, - menuAction__revoke: undefined, + action__add: 'Neuen API-Key hinzufügen', + action__search: 'Suche', + 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.', + formTitle: 'Neuen API-Key hinzufügen', + menuAction__revoke: 'API-Key widerrufen', revokeConfirmation: { - formButtonPrimary__revoke: undefined, - formHint: undefined, - formTitle: undefined, + formButtonPrimary__revoke: 'API-Key widerrufen', + formHint: 'Sind Sie sicher, dass Sie diesen API-Key löschen wollen?', + formTitle: 'API-Key "{{apiKeyName}}" widerrufen?', }, }, backButton: 'Zurück', From aa69753f1b6ea34335bae04d5a8038b0b98a5874 Mon Sep 17 00:00:00 2001 From: Robert Soriano Date: Tue, 3 Jun 2025 08:08:11 -0700 Subject: [PATCH 079/116] Update packages/localizations/src/de-DE.ts Co-authored-by: Lennart --- packages/localizations/src/de-DE.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/localizations/src/de-DE.ts b/packages/localizations/src/de-DE.ts index 8c49f14a854..53db8b62931 100644 --- a/packages/localizations/src/de-DE.ts +++ b/packages/localizations/src/de-DE.ts @@ -152,9 +152,9 @@ export const deDE: LocalizationResource = { formFieldHintText__optional: 'Optional', formFieldHintText__slug: 'Der Slug ist eine für Menschen lesbare ID. Sie muss einzigartig sein und wird oft in URLs verwendet.', - formFieldInputPlaceholder__apiKeyDescription: undefined, - formFieldInputPlaceholder__apiKeyExpirationDate: undefined, - formFieldInputPlaceholder__apiKeyName: undefined, + formFieldInputPlaceholder__apiKeyDescription: 'Geben Sie eine Beschreibung an', + formFieldInputPlaceholder__apiKeyExpirationDate: 'Geben Sie ein Ablaufdatum an', + formFieldInputPlaceholder__apiKeyName: 'Geben Sie einen Namen an', formFieldInputPlaceholder__backupCode: 'Sicherheitscode eingeben', formFieldInputPlaceholder__confirmDeletionUserAccount: 'Konto löschen', formFieldInputPlaceholder__emailAddress: 'E-Mail-Adresse eingeben', From 87926e4c19cdf8ba20d567a8163af1fa587db154 Mon Sep 17 00:00:00 2001 From: Robert Soriano Date: Tue, 3 Jun 2025 08:08:32 -0700 Subject: [PATCH 080/116] Update .changeset/ninety-candles-sleep.md Co-authored-by: Lennart --- .changeset/ninety-candles-sleep.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.changeset/ninety-candles-sleep.md b/.changeset/ninety-candles-sleep.md index 5159bef7661..bb3a4e60c90 100644 --- a/.changeset/ninety-candles-sleep.md +++ b/.changeset/ninety-candles-sleep.md @@ -4,4 +4,4 @@ '@clerk/clerk-react': minor --- -Introduce early access `` component. +Add `` component. This component will initially be in early access and not recommended for production usage just yet. From 28894eb608e34fc70aa6d163b1e5ac2f3681149e Mon Sep 17 00:00:00 2001 From: Robert Soriano Date: Tue, 3 Jun 2025 08:08:39 -0700 Subject: [PATCH 081/116] Update .changeset/fluffy-numbers-stick.md Co-authored-by: Lennart --- .changeset/fluffy-numbers-stick.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.changeset/fluffy-numbers-stick.md b/.changeset/fluffy-numbers-stick.md index 8ae9eb674ab..5b2c494922f 100644 --- a/.changeset/fluffy-numbers-stick.md +++ b/.changeset/fluffy-numbers-stick.md @@ -3,4 +3,4 @@ '@clerk/types': patch --- -Introduce types and locales for `` component +Add TypeScript types and en-US localization for upcoming `` component. This component will initially be in early access. From aac35e736ff80fcc2d82a1681a1868429c178c1a Mon Sep 17 00:00:00 2001 From: Robert Soriano Date: Tue, 3 Jun 2025 08:22:08 -0700 Subject: [PATCH 082/116] Apply suggestions from code review Co-authored-by: Lennart --- packages/localizations/src/de-DE.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/localizations/src/de-DE.ts b/packages/localizations/src/de-DE.ts index 53db8b62931..947d2d329c8 100644 --- a/packages/localizations/src/de-DE.ts +++ b/packages/localizations/src/de-DE.ts @@ -169,10 +169,10 @@ export const deDE: LocalizationResource = { formFieldInputPlaceholder__password: 'Passwort eingeben', formFieldInputPlaceholder__phoneNumber: 'Telefonnummer eingeben', formFieldInputPlaceholder__username: 'Benutzername eingeben', - formFieldLabel__apiKeyDescription: undefined, - formFieldLabel__apiKeyExpiration: undefined, - formFieldLabel__apiKeyExpirationDate: undefined, - formFieldLabel__apiKeyName: undefined, + 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', formFieldLabel__confirmDeletion: 'Bestätigung', @@ -219,7 +219,7 @@ export const deDE: LocalizationResource = { }, organizationProfile: { apiKeysPage: { - title: undefined, + title: 'API-Keys', }, badge__automaticInvitation: 'Automatische Einladungen', badge__automaticSuggestion: 'Automatische Vorschläge', From 3f7b3b27b4fd10d5910b0e5f89e014469d428207 Mon Sep 17 00:00:00 2001 From: wobsoriano Date: Tue, 3 Jun 2025 12:29:38 -0700 Subject: [PATCH 083/116] fix: pass correct subject when used inside pages --- packages/clerk-js/src/ui/components/ApiKeys/ApiKeys.tsx | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/clerk-js/src/ui/components/ApiKeys/ApiKeys.tsx b/packages/clerk-js/src/ui/components/ApiKeys/ApiKeys.tsx index 94b5dde0d92..0fb6e4f5f7e 100644 --- a/packages/clerk-js/src/ui/components/ApiKeys/ApiKeys.tsx +++ b/packages/clerk-js/src/ui/components/ApiKeys/ApiKeys.tsx @@ -53,7 +53,10 @@ export const APIKeysPage = ({ subject, perPage, revokeModalRoot }: APIKeysPagePr const handleCreateApiKey = async (params: OnCreateParams, closeCardFn: () => void) => { try { - await createApiKey(params); + await createApiKey({ + ...params, + subject, + }); closeCardFn(); card.setError(undefined); } catch (err: any) { From d54ea6ca8b8a4a91f07e27a2ef2382e1bef25f71 Mon Sep 17 00:00:00 2001 From: wobsoriano Date: Tue, 3 Jun 2025 12:32:33 -0700 Subject: [PATCH 084/116] chore: update bundlewatch config --- packages/clerk-js/bundlewatch.config.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/clerk-js/bundlewatch.config.json b/packages/clerk-js/bundlewatch.config.json index 032fd11c408..f0c92af2ecd 100644 --- a/packages/clerk-js/bundlewatch.config.json +++ b/packages/clerk-js/bundlewatch.config.json @@ -1,9 +1,9 @@ { "files": [ { "path": "./dist/clerk.js", "maxSize": "605kB" }, - { "path": "./dist/clerk.browser.js", "maxSize": "69KB" }, + { "path": "./dist/clerk.browser.js", "maxSize": "69.56KB" }, { "path": "./dist/clerk.legacy.browser.js", "maxSize": "113KB" }, - { "path": "./dist/clerk.headless*.js", "maxSize": "52KB" }, + { "path": "./dist/clerk.headless*.js", "maxSize": "52.44KB" }, { "path": "./dist/ui-common*.js", "maxSize": "106KB" }, { "path": "./dist/vendors*.js", "maxSize": "39.8KB" }, { "path": "./dist/coinbase*.js", "maxSize": "38KB" }, From f0efd21787157b46b1ff2dafb39378f1a9f13c2d Mon Sep 17 00:00:00 2001 From: wobsoriano Date: Tue, 3 Jun 2025 12:49:10 -0700 Subject: [PATCH 085/116] remove barrel files --- packages/clerk-js/src/ui/components/UserProfile/index.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/clerk-js/src/ui/components/UserProfile/index.tsx b/packages/clerk-js/src/ui/components/UserProfile/index.tsx index 2267a695244..95749f80b9c 100644 --- a/packages/clerk-js/src/ui/components/UserProfile/index.tsx +++ b/packages/clerk-js/src/ui/components/UserProfile/index.tsx @@ -34,7 +34,6 @@ const _UserProfile = (_: UserProfileProps) => { const AuthenticatedRoutes = withCoreUserGuard(() => { const contentRef = React.useRef(null); - return ( From 24cb76d5d6bcb97ee1f2c0e830599a7387d8e2b2 Mon Sep 17 00:00:00 2001 From: wobsoriano Date: Tue, 3 Jun 2025 13:17:37 -0700 Subject: [PATCH 086/116] chore: avoid barrel files --- .../src/ui/components/ApiKeys/ApiKeys.tsx | 15 +++++++++------ .../src/ui/components/ApiKeys/ApiKeysTable.tsx | 12 ++++++------ .../ui/components/ApiKeys/CreateApiKeyForm.tsx | 13 ++++++++----- .../ApiKeys/RevokeAPIKeyConfirmationModal.tsx | 10 +++++++--- .../clerk-js/src/ui/components/ApiKeys/index.tsx | 1 - .../OrganizationApiKeysPage.tsx | 10 ++++++---- .../src/ui/components/UserProfile/ApiKeysPage.tsx | 10 ++++++---- .../clerk-js/src/ui/lazyModules/components.ts | 2 +- packages/types/src/appearance.ts | 4 ++-- 9 files changed, 45 insertions(+), 32 deletions(-) delete mode 100644 packages/clerk-js/src/ui/components/ApiKeys/index.tsx diff --git a/packages/clerk-js/src/ui/components/ApiKeys/ApiKeys.tsx b/packages/clerk-js/src/ui/components/ApiKeys/ApiKeys.tsx index 0fb6e4f5f7e..1e03f83f04f 100644 --- a/packages/clerk-js/src/ui/components/ApiKeys/ApiKeys.tsx +++ b/packages/clerk-js/src/ui/components/ApiKeys/ApiKeys.tsx @@ -4,12 +4,15 @@ import type { CreateAPIKeyParams } from '@clerk/types'; import { lazy, useState } from 'react'; import useSWRMutation from 'swr/mutation'; -import { useApiKeysContext, withCoreUserGuard } from '../../contexts'; -import { Box, Button, Col, Flex, Flow, Icon, localizationKeys, useLocalizations } from '../../customizables'; -import { InputWithIcon, Pagination, useCardState, withCardStateProvider } from '../../elements'; -import { Action } from '../../elements/Action'; -import { MagnifyingGlass } from '../../icons'; -import { mqu } from '../../styledSystem'; +import { useApiKeysContext, withCoreUserGuard } from '@/ui/contexts'; +import { Box, Button, Col, Flex, Flow, Icon, localizationKeys, useLocalizations } from '@/ui/customizables'; +import { Action } from '@/ui/elements/Action'; +import { useCardState, withCardStateProvider } from '@/ui/elements/contexts'; +import { InputWithIcon } from '@/ui/elements/InputWithIcon'; +import { Pagination } from '@/ui/elements/Pagination'; +import { MagnifyingGlass } from '@/ui/icons'; +import { mqu } from '@/ui/styledSystem'; + import { ApiKeysTable } from './ApiKeysTable'; import type { OnCreateParams } from './CreateApiKeyForm'; import { CreateApiKeyForm } from './CreateApiKeyForm'; diff --git a/packages/clerk-js/src/ui/components/ApiKeys/ApiKeysTable.tsx b/packages/clerk-js/src/ui/components/ApiKeys/ApiKeysTable.tsx index 5e25d16a137..259b3b318ad 100644 --- a/packages/clerk-js/src/ui/components/ApiKeys/ApiKeysTable.tsx +++ b/packages/clerk-js/src/ui/components/ApiKeys/ApiKeysTable.tsx @@ -3,7 +3,6 @@ import type { APIKeyResource } from '@clerk/types'; import { useEffect, useState } from 'react'; import useSWR from 'swr'; -import { timeAgo } from '../../../utils/date'; import { Box, Button, @@ -20,11 +19,12 @@ import { Th, Thead, Tr, -} from '../../customizables'; -import { ThreeDotsMenu } from '../../elements'; -import { useClipboard } from '../../hooks'; -import { Check, ClipboardOutline, Eye, EyeSlash } from '../../icons'; -import { common, mqu } from '../../styledSystem'; +} from '@/ui/customizables'; +import { ThreeDotsMenu } from '@/ui/elements/ThreeDotsMenu'; +import { useClipboard } from '@/ui/hooks'; +import { Check, ClipboardOutline, Eye, EyeSlash } from '@/ui/icons'; +import { common, mqu } from '@/ui/styledSystem'; +import { timeAgo } from '@/utils/date'; const useApiKeySecret = ({ apiKeyID, enabled }: { apiKeyID: string; enabled: boolean }) => { const clerk = useClerk(); diff --git a/packages/clerk-js/src/ui/components/ApiKeys/CreateApiKeyForm.tsx b/packages/clerk-js/src/ui/components/ApiKeys/CreateApiKeyForm.tsx index 02d002bfebf..cbb01a1848d 100644 --- a/packages/clerk-js/src/ui/components/ApiKeys/CreateApiKeyForm.tsx +++ b/packages/clerk-js/src/ui/components/ApiKeys/CreateApiKeyForm.tsx @@ -1,10 +1,13 @@ import React, { useState } from 'react'; -import { Box, Button, Col, Flex, FormLabel, localizationKeys, Text } from '../../customizables'; -import { Form, FormButtons, FormContainer, SegmentedControl } from '../../elements'; -import { useActionContext } from '../../elements/Action/ActionRoot'; -import { mqu } from '../../styledSystem'; -import { useFormControl } from '../../utils'; +import { Box, Button, Col, Flex, FormLabel, localizationKeys, Text } 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 { mqu } from '@/ui/styledSystem'; +import { useFormControl } from '@/ui/utils/useFormControl'; export type OnCreateParams = { name: string; description?: string; expiration: number | undefined }; diff --git a/packages/clerk-js/src/ui/components/ApiKeys/RevokeAPIKeyConfirmationModal.tsx b/packages/clerk-js/src/ui/components/ApiKeys/RevokeAPIKeyConfirmationModal.tsx index fa06548a333..b8997f83b67 100644 --- a/packages/clerk-js/src/ui/components/ApiKeys/RevokeAPIKeyConfirmationModal.tsx +++ b/packages/clerk-js/src/ui/components/ApiKeys/RevokeAPIKeyConfirmationModal.tsx @@ -1,9 +1,13 @@ import { useClerk } from '@clerk/shared/react'; import { useSWRConfig } from 'swr'; -import { Card, Form, FormButtons, FormContainer, Modal } from '../../elements'; -import { localizationKeys } from '../../localization'; -import { useFormControl } from '../../utils'; +import { Card } from '@/ui/elements/Card'; +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 { useFormControl } from '@/ui/utils'; type RevokeAPIKeyConfirmationModalProps = { isOpen: boolean; diff --git a/packages/clerk-js/src/ui/components/ApiKeys/index.tsx b/packages/clerk-js/src/ui/components/ApiKeys/index.tsx deleted file mode 100644 index a83ede2a3d7..00000000000 --- a/packages/clerk-js/src/ui/components/ApiKeys/index.tsx +++ /dev/null @@ -1 +0,0 @@ -export * from './ApiKeys'; diff --git a/packages/clerk-js/src/ui/components/OrganizationProfile/OrganizationApiKeysPage.tsx b/packages/clerk-js/src/ui/components/OrganizationProfile/OrganizationApiKeysPage.tsx index 3d816fe8858..230c0835a5f 100644 --- a/packages/clerk-js/src/ui/components/OrganizationProfile/OrganizationApiKeysPage.tsx +++ b/packages/clerk-js/src/ui/components/OrganizationProfile/OrganizationApiKeysPage.tsx @@ -1,9 +1,11 @@ import { useOrganization } from '@clerk/shared/react'; -import { ApiKeysContext } from '../../contexts'; -import { Col, localizationKeys } from '../../customizables'; -import { Header, useUnsafeNavbarContext } from '../../elements'; -import { APIKeysPage } from '../ApiKeys'; +import { ApiKeysContext } from '@/ui/contexts'; +import { Col, localizationKeys } from '@/ui/customizables'; +import { Header } from '@/ui/elements/Header'; +import { useUnsafeNavbarContext } from '@/ui/elements/Navbar'; + +import { APIKeysPage } from '../ApiKeys/ApiKeys'; export const OrganizationAPIKeysPage = () => { const { organization } = useOrganization(); diff --git a/packages/clerk-js/src/ui/components/UserProfile/ApiKeysPage.tsx b/packages/clerk-js/src/ui/components/UserProfile/ApiKeysPage.tsx index a2d1f1fbbf0..981b97ca035 100644 --- a/packages/clerk-js/src/ui/components/UserProfile/ApiKeysPage.tsx +++ b/packages/clerk-js/src/ui/components/UserProfile/ApiKeysPage.tsx @@ -1,9 +1,11 @@ import { useUser } from '@clerk/shared/react'; -import { ApiKeysContext } from '../../contexts'; -import { Col, localizationKeys } from '../../customizables'; -import { Header, useUnsafeNavbarContext } from '../../elements'; -import { APIKeysPage as APIKeysPageInternal } from '../ApiKeys'; +import { ApiKeysContext } from '@/ui/contexts'; +import { Col, localizationKeys } from '@/ui/customizables'; +import { Header } from '@/ui/elements/Header'; +import { useUnsafeNavbarContext } from '@/ui/elements/Navbar'; + +import { APIKeysPage as APIKeysPageInternal } from '../ApiKeys/ApiKeys'; export const APIKeysPage = () => { const { user } = useUser(); diff --git a/packages/clerk-js/src/ui/lazyModules/components.ts b/packages/clerk-js/src/ui/lazyModules/components.ts index 23a090f97ea..fd9e484bd33 100644 --- a/packages/clerk-js/src/ui/lazyModules/components.ts +++ b/packages/clerk-js/src/ui/lazyModules/components.ts @@ -21,7 +21,7 @@ const componentImportPaths = { Checkout: () => import(/* webpackChunkName: "checkout" */ '../components/Checkout'), SessionTasks: () => import(/* webpackChunkName: "sessionTasks" */ '../components/SessionTasks'), PlanDetails: () => import(/* webpackChunkName: "planDetails" */ '../components/Plans'), - APIKeys: () => import(/* webpackChunkName: "apiKeys" */ '../components/ApiKeys'), + APIKeys: () => import(/* webpackChunkName: "apiKeys" */ '../components/ApiKeys/ApiKeys'), } as const; export const SignIn = lazy(() => componentImportPaths.SignIn().then(module => ({ default: module.SignIn }))); diff --git a/packages/types/src/appearance.ts b/packages/types/src/appearance.ts index 5ff06adcc6f..efa210b07ac 100644 --- a/packages/types/src/appearance.ts +++ b/packages/types/src/appearance.ts @@ -833,7 +833,7 @@ export type WaitlistTheme = Theme; export type PricingTableTheme = Theme; export type CheckoutTheme = Theme; export type PlanDetailTheme = Theme; -export type ApiKeysTheme = Theme; +export type APIKeysTheme = Theme; type GlobalAppearanceOptions = { /** @@ -899,7 +899,7 @@ export type Appearance = T & */ checkout?: T; /** - * Theme overrides that only apply to the `` component + * Theme overrides that only apply to the `` component */ apiKeys?: T; }; From 062b2fe2ee4e7a435f175812dd6ce47cda934528 Mon Sep 17 00:00:00 2001 From: wobsoriano Date: Thu, 5 Jun 2025 14:40:23 -0700 Subject: [PATCH 087/116] fix incorrect merge --- packages/clerk-js/src/core/clerk.ts | 17 +++++++++++++++-- .../ui/contexts/ClerkUIComponentsContext.tsx | 8 +++++++- packages/react/src/isomorphicClerk.ts | 1 - packages/types/src/appearance.ts | 1 + 4 files changed, 23 insertions(+), 4 deletions(-) diff --git a/packages/clerk-js/src/core/clerk.ts b/packages/clerk-js/src/core/clerk.ts index e59f40756dc..529421b6856 100644 --- a/packages/clerk-js/src/core/clerk.ts +++ b/packages/clerk-js/src/core/clerk.ts @@ -1069,7 +1069,17 @@ export class Clerk implements ClerkInterface { * @param targetNode Target to mount the APIKeys component. * @param props Configuration parameters. */ - mountApiKeys: (targetNode: HTMLDivElement, props?: APIKeysProps) => void; + public mountApiKeys = (node: HTMLDivElement, props?: APIKeysProps) => { + this.assertComponentsReady(this.#componentControls); + void this.#componentControls.ensureMounted({ preloadHint: 'APIKeys' }).then(controls => + controls.mountComponent({ + name: 'APIKeys', + appearanceKey: 'apiKeys', + node, + props, + }), + ); + }; /** * @experimental @@ -1080,7 +1090,10 @@ export class Clerk implements ClerkInterface { * * @param targetNode Target node to unmount the ApiKeys component from. */ - unmountApiKeys: (targetNode: HTMLDivElement) => void; + public unmountApiKeys = (node: HTMLDivElement) => { + this.assertComponentsReady(this.#componentControls); + void this.#componentControls.ensureMounted().then(controls => controls.unmountComponent({ node })); + }; /** * `setActive` can be used to set the active session and/or organization. diff --git a/packages/clerk-js/src/ui/contexts/ClerkUIComponentsContext.tsx b/packages/clerk-js/src/ui/contexts/ClerkUIComponentsContext.tsx index a0015374cd8..d09ba7c7e1f 100644 --- a/packages/clerk-js/src/ui/contexts/ClerkUIComponentsContext.tsx +++ b/packages/clerk-js/src/ui/contexts/ClerkUIComponentsContext.tsx @@ -1,4 +1,10 @@ -import type { __internal_OAuthConsentProps, APIKeysProps, PricingTableProps, UserButtonProps, WaitlistProps } from '@clerk/types'; +import type { + __internal_OAuthConsentProps, + APIKeysProps, + PricingTableProps, + UserButtonProps, + WaitlistProps, +} from '@clerk/types'; import type { ReactNode } from 'react'; import type { AvailableComponentName, AvailableComponentProps } from '../types'; diff --git a/packages/react/src/isomorphicClerk.ts b/packages/react/src/isomorphicClerk.ts index 9500dfc09d2..440baf12af7 100644 --- a/packages/react/src/isomorphicClerk.ts +++ b/packages/react/src/isomorphicClerk.ts @@ -617,7 +617,6 @@ export class IsomorphicClerk implements IsomorphicLoadedClerk { clerkjs.mountPricingTable(node, props); }); - this.premountApiKeysNodes.forEach((props, node) => { clerkjs.mountApiKeys(node, props); }); diff --git a/packages/types/src/appearance.ts b/packages/types/src/appearance.ts index aa3f9120b92..ebe51637b70 100644 --- a/packages/types/src/appearance.ts +++ b/packages/types/src/appearance.ts @@ -903,6 +903,7 @@ export type Appearance = T & * Theme overrides that only apply to the `` component */ apiKeys?: T; + /** * Theme overrides that only apply to the `` component */ __internal_oauthConsent?: T; From a996bbc1d32b9a47600d83bc4786aadbc75bece0 Mon Sep 17 00:00:00 2001 From: wobsoriano Date: Thu, 5 Jun 2025 14:52:16 -0700 Subject: [PATCH 088/116] mark methods as experimental --- packages/clerk-js/sandbox/template.html | 2 +- packages/localizations/src/en-US.ts | 2 +- packages/types/src/clerk.ts | 4 ++++ 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/packages/clerk-js/sandbox/template.html b/packages/clerk-js/sandbox/template.html index 9122941cf8e..cb2d65d427a 100644 --- a/packages/clerk-js/sandbox/template.html +++ b/packages/clerk-js/sandbox/template.html @@ -453,7 +453,7 @@