From e401daa9ab067a6c63d2708f3afcd795db23a325 Mon Sep 17 00:00:00 2001 From: Laura Beatris <48022589+LauraBeatris@users.noreply.github.com> Date: Tue, 16 Dec 2025 21:58:59 -0300 Subject: [PATCH 01/17] Add `OrganizationCreationDefaults` resource --- .../resources/OrganizationCreationDefaults.ts | 42 +++++++++++++++++++ packages/shared/src/types/index.ts | 3 +- .../src/types/organizationCreationDefaults.ts | 20 +++++++++ packages/shared/src/types/snapshots.ts | 3 ++ 4 files changed, 67 insertions(+), 1 deletion(-) create mode 100644 packages/clerk-js/src/core/resources/OrganizationCreationDefaults.ts create mode 100644 packages/shared/src/types/organizationCreationDefaults.ts diff --git a/packages/clerk-js/src/core/resources/OrganizationCreationDefaults.ts b/packages/clerk-js/src/core/resources/OrganizationCreationDefaults.ts new file mode 100644 index 00000000000..83b927b68dd --- /dev/null +++ b/packages/clerk-js/src/core/resources/OrganizationCreationDefaults.ts @@ -0,0 +1,42 @@ +import type { + OrganizationCreationDefaultsJSON, + OrganizationCreationDefaultsJSONSnapshot, + OrganizationCreationDefaultsResource, +} from '@clerk/shared/types'; + +import { BaseResource } from './internal'; + +export class OrganizationCreationDefaults extends BaseResource implements OrganizationCreationDefaultsResource { + creationAdvisory: { + type: 'existing_org_with_domain'; + severity: 'warning'; + } | null = null; + + public constructor(data: OrganizationCreationDefaultsJSON | OrganizationCreationDefaultsJSONSnapshot | null = null) { + super(); + this.fromJSON(data); + } + + protected fromJSON(data: OrganizationCreationDefaultsJSON | OrganizationCreationDefaultsJSONSnapshot | null): this { + if (!data) { + return this; + } + + if (data.creation_advisory) { + this.creationAdvisory = this.withDefault(data.creation_advisory, this.creationAdvisory ?? null); + } + + return this; + } + + public __internal_toSnapshot(): OrganizationCreationDefaultsJSONSnapshot { + return { + creation_advisory: this.creationAdvisory + ? { + type: this.creationAdvisory.type, + severity: this.creationAdvisory.severity, + } + : null, + } as unknown as OrganizationCreationDefaultsJSONSnapshot; + } +} diff --git a/packages/shared/src/types/index.ts b/packages/shared/src/types/index.ts index d1f50cfde7c..068e5728cae 100644 --- a/packages/shared/src/types/index.ts +++ b/packages/shared/src/types/index.ts @@ -13,6 +13,7 @@ export type * from './customPages'; export type * from './deletedObject'; export type * from './devtools'; export type * from './displayConfig'; +export type * from './elementIds'; export type * from './emailAddress'; export type * from './enterpriseAccount'; export type * from './environment'; @@ -32,6 +33,7 @@ export type * from './localization'; export type * from './multiDomain'; export type * from './oauth'; export type * from './organization'; +export type * from './organizationCreationDefaults'; export type * from './organizationDomain'; export type * from './organizationInvitation'; export type * from './organizationMembership'; @@ -49,7 +51,6 @@ export type * from './protectConfig'; export type * from './redirects'; export type * from './resource'; export type * from './role'; -export type * from './elementIds'; export type * from './router'; /** * TODO @revamp-hooks: Drop this in the next major release. diff --git a/packages/shared/src/types/organizationCreationDefaults.ts b/packages/shared/src/types/organizationCreationDefaults.ts new file mode 100644 index 00000000000..e510af241f8 --- /dev/null +++ b/packages/shared/src/types/organizationCreationDefaults.ts @@ -0,0 +1,20 @@ +import type { ClerkResourceJSON } from './json'; +import type { ClerkResource } from './resource'; + +export type OrganizationCreationAdvisoryType = 'existing_org_with_domain'; + +export type OrganizationCreationAdvisorySeverity = 'warning'; + +export interface OrganizationCreationDefaultsJSON extends ClerkResourceJSON { + creation_advisory: { + type: OrganizationCreationAdvisoryType; + severity: OrganizationCreationAdvisorySeverity; + } | null; +} + +export interface OrganizationCreationDefaultsResource extends ClerkResource { + creationAdvisory: { + type: OrganizationCreationAdvisoryType; + severity: OrganizationCreationAdvisorySeverity; + } | null; +} diff --git a/packages/shared/src/types/snapshots.ts b/packages/shared/src/types/snapshots.ts index 6db74374c44..a1d239c329f 100644 --- a/packages/shared/src/types/snapshots.ts +++ b/packages/shared/src/types/snapshots.ts @@ -28,6 +28,7 @@ import type { VerificationJSON, Web3WalletJSON, } from './json'; +import type { OrganizationCreationDefaultsJSON } from './organizationCreationDefaults'; import type { OrganizationSettingsJSON } from './organizationSettings'; import type { ProtectConfigJSON } from './protectConfig'; import type { SignInJSON } from './signIn'; @@ -143,6 +144,8 @@ export type OrganizationMembershipJSONSnapshot = OrganizationMembershipJSON; export type OrganizationSettingsJSONSnapshot = OrganizationSettingsJSON; +export type OrganizationCreationDefaultsJSONSnapshot = OrganizationCreationDefaultsJSON; + export type PasskeyJSONSnapshot = Override; export type PhoneNumberJSONSnapshot = Override< From 700303bac0164c743c82328d5e7769e8821bf351 Mon Sep 17 00:00:00 2001 From: Laura Beatris <48022589+LauraBeatris@users.noreply.github.com> Date: Tue, 16 Dec 2025 22:02:02 -0300 Subject: [PATCH 02/17] Add TODOs for UI tests --- .../__tests__/TaskChooseOrganization.test.tsx | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/packages/ui/src/components/SessionTasks/tasks/TaskChooseOrganization/__tests__/TaskChooseOrganization.test.tsx b/packages/ui/src/components/SessionTasks/tasks/TaskChooseOrganization/__tests__/TaskChooseOrganization.test.tsx index dbe835f3540..987235c642b 100644 --- a/packages/ui/src/components/SessionTasks/tasks/TaskChooseOrganization/__tests__/TaskChooseOrganization.test.tsx +++ b/packages/ui/src/components/SessionTasks/tasks/TaskChooseOrganization/__tests__/TaskChooseOrganization.test.tsx @@ -353,4 +353,10 @@ describe('TaskChooseOrganization', () => { expect(await findByText('Existing Org')).toBeInTheDocument(); }); }); + + describe('with organization creation defaults', () => { + it.todo('displays warning when organization already exists for user email domain'); + + it.todo('prefills create organization form with defaults'); + }); }); From 15c8470dc263c9aedf6d52595e849bd9c9f50ca9 Mon Sep 17 00:00:00 2001 From: Laura Beatris <48022589+LauraBeatris@users.noreply.github.com> Date: Wed, 17 Dec 2025 14:04:34 -0300 Subject: [PATCH 03/17] Add alert for creation advisory --- .../resources/OrganizationCreationDefaults.ts | 10 ++++++++ packages/localizations/src/en-US.ts | 4 +++ packages/shared/src/types/localization.ts | 3 +++ .../CreateOrganizationScreen.tsx | 14 +++++++++++ .../OrganizationCreationDefaultsAlert.tsx | 25 +++++++++++++++++++ 5 files changed, 56 insertions(+) create mode 100644 packages/ui/src/components/SessionTasks/tasks/TaskChooseOrganization/OrganizationCreationDefaultsAlert.tsx diff --git a/packages/clerk-js/src/core/resources/OrganizationCreationDefaults.ts b/packages/clerk-js/src/core/resources/OrganizationCreationDefaults.ts index 83b927b68dd..16ac507e142 100644 --- a/packages/clerk-js/src/core/resources/OrganizationCreationDefaults.ts +++ b/packages/clerk-js/src/core/resources/OrganizationCreationDefaults.ts @@ -29,6 +29,16 @@ export class OrganizationCreationDefaults extends BaseResource implements Organi return this; } + static async retrieve(): Promise { + return await BaseResource._fetch({ + path: '/me/organization_creation_defaults', + method: 'GET', + }).then(res => { + const data = res?.response as unknown as OrganizationCreationDefaultsJSON; + return new OrganizationCreationDefaults(data); + }); + } + public __internal_toSnapshot(): OrganizationCreationDefaultsJSONSnapshot { return { creation_advisory: this.creationAdvisory diff --git a/packages/localizations/src/en-US.ts b/packages/localizations/src/en-US.ts index 8f8cba4e707..9eb252dafb2 100644 --- a/packages/localizations/src/en-US.ts +++ b/packages/localizations/src/en-US.ts @@ -884,6 +884,10 @@ export const enUS: LocalizationResource = { actionLink: 'Sign out', actionText: 'Signed in as {{identifier}}', }, + alerts: { + existingOrgWithDomain: + 'An organization already exists for the detected company name and email domain. Join by invitation.', + }, }, taskResetPassword: { formButtonPrimary: 'Reset Password', diff --git a/packages/shared/src/types/localization.ts b/packages/shared/src/types/localization.ts index 806826143d8..7b5e19d33df 100644 --- a/packages/shared/src/types/localization.ts +++ b/packages/shared/src/types/localization.ts @@ -1314,6 +1314,9 @@ export type __internal_LocalizationResource = { title: LocalizationValue; subtitle: LocalizationValue; }; + alerts: { + existingOrgWithDomain: LocalizationValue; + }; }; taskResetPassword: { title: LocalizationValue; diff --git a/packages/ui/src/components/SessionTasks/tasks/TaskChooseOrganization/CreateOrganizationScreen.tsx b/packages/ui/src/components/SessionTasks/tasks/TaskChooseOrganization/CreateOrganizationScreen.tsx index 2cf177d11a4..75d7c0baa07 100644 --- a/packages/ui/src/components/SessionTasks/tasks/TaskChooseOrganization/CreateOrganizationScreen.tsx +++ b/packages/ui/src/components/SessionTasks/tasks/TaskChooseOrganization/CreateOrganizationScreen.tsx @@ -4,6 +4,7 @@ import type { CreateOrganizationParams } from '@clerk/shared/types'; import { useEnvironment } from '@/ui/contexts'; import { useSessionTasksContext, useTaskChooseOrganizationContext } from '@/ui/contexts/components/SessionTasks'; import { localizationKeys } from '@/ui/customizables'; +// or from '@/ui/elements' import { useCardState } from '@/ui/elements/contexts'; import { Form } from '@/ui/elements/Form'; import { FormButtonContainer } from '@/ui/elements/FormButtons'; @@ -14,6 +15,17 @@ import { handleError } from '@/ui/utils/errorHandler'; import { useFormControl } from '@/ui/utils/useFormControl'; import { organizationListParams } from '../../../OrganizationSwitcher/utils'; +import { OrganizationCreationDefaultsAlert } from './OrganizationCreationDefaultsAlert'; + +// TODO: Replace with actual API call to OrganizationCreationDefaults.retrieve() +const organizationCreationDefaults = { + creationAdvisory: { + type: 'existing_org_with_domain' as const, + severity: 'warning' as const, + }, + pathRoot: '', + reload: () => Promise.resolve({} as any), +}; type CreateOrganizationScreenProps = { onCancel?: () => void; @@ -88,7 +100,9 @@ export const CreateOrganizationScreen = (props: CreateOrganizationScreenProps) = + ({ padding: `${t.space.$none} ${t.space.$10} ${t.space.$8}` })}> + + ); +} + +const advisoryTypeToLocalizationKey: Record = { + existing_org_with_domain: localizationKeys('taskChooseOrganization.alerts.existingOrgWithDomain'), +}; From 0883107a1f69f763c0cf3f45a15477e4eb7076a3 Mon Sep 17 00:00:00 2001 From: Laura Beatris <48022589+LauraBeatris@users.noreply.github.com> Date: Wed, 17 Dec 2025 14:23:03 -0300 Subject: [PATCH 04/17] Update entity to include form defaults --- .../resources/OrganizationCreationDefaults.ts | 30 ++++++++++++++----- .../src/types/organizationCreationDefaults.ts | 12 ++++++-- .../CreateOrganizationForm.tsx | 1 + .../CreateOrganizationScreen.tsx | 3 +- .../OrganizationCreationDefaultsAlert.tsx | 6 ++-- 5 files changed, 37 insertions(+), 15 deletions(-) diff --git a/packages/clerk-js/src/core/resources/OrganizationCreationDefaults.ts b/packages/clerk-js/src/core/resources/OrganizationCreationDefaults.ts index 16ac507e142..f872602acb5 100644 --- a/packages/clerk-js/src/core/resources/OrganizationCreationDefaults.ts +++ b/packages/clerk-js/src/core/resources/OrganizationCreationDefaults.ts @@ -1,4 +1,6 @@ import type { + OrganizationCreationAdvisorySeverity, + OrganizationCreationAdvisoryType, OrganizationCreationDefaultsJSON, OrganizationCreationDefaultsJSONSnapshot, OrganizationCreationDefaultsResource, @@ -7,10 +9,17 @@ import type { import { BaseResource } from './internal'; export class OrganizationCreationDefaults extends BaseResource implements OrganizationCreationDefaultsResource { - creationAdvisory: { - type: 'existing_org_with_domain'; - severity: 'warning'; + advisory: { + type: OrganizationCreationAdvisoryType; + severity: OrganizationCreationAdvisorySeverity; } | null = null; + form: { + name: string; + slug: string; + } = { + name: '', + slug: '', + }; public constructor(data: OrganizationCreationDefaultsJSON | OrganizationCreationDefaultsJSONSnapshot | null = null) { super(); @@ -22,8 +31,13 @@ export class OrganizationCreationDefaults extends BaseResource implements Organi return this; } - if (data.creation_advisory) { - this.creationAdvisory = this.withDefault(data.creation_advisory, this.creationAdvisory ?? null); + if (data.advisory) { + this.advisory = this.withDefault(data.advisory, this.advisory ?? null); + } + + if (data.form) { + this.form.name = this.withDefault(data.form.name, this.form.name); + this.form.slug = this.withDefault(data.form.slug, this.form.slug); } return this; @@ -41,10 +55,10 @@ export class OrganizationCreationDefaults extends BaseResource implements Organi public __internal_toSnapshot(): OrganizationCreationDefaultsJSONSnapshot { return { - creation_advisory: this.creationAdvisory + advisory: this.advisory ? { - type: this.creationAdvisory.type, - severity: this.creationAdvisory.severity, + type: this.advisory.type, + severity: this.advisory.severity, } : null, } as unknown as OrganizationCreationDefaultsJSONSnapshot; diff --git a/packages/shared/src/types/organizationCreationDefaults.ts b/packages/shared/src/types/organizationCreationDefaults.ts index e510af241f8..badc42917d0 100644 --- a/packages/shared/src/types/organizationCreationDefaults.ts +++ b/packages/shared/src/types/organizationCreationDefaults.ts @@ -6,15 +6,23 @@ export type OrganizationCreationAdvisoryType = 'existing_org_with_domain'; export type OrganizationCreationAdvisorySeverity = 'warning'; export interface OrganizationCreationDefaultsJSON extends ClerkResourceJSON { - creation_advisory: { + advisory: { type: OrganizationCreationAdvisoryType; severity: OrganizationCreationAdvisorySeverity; } | null; + form: { + name: string; + slug: string; + }; } export interface OrganizationCreationDefaultsResource extends ClerkResource { - creationAdvisory: { + advisory: { type: OrganizationCreationAdvisoryType; severity: OrganizationCreationAdvisorySeverity; } | null; + form: { + name: string; + slug: string; + }; } diff --git a/packages/ui/src/components/CreateOrganization/CreateOrganizationForm.tsx b/packages/ui/src/components/CreateOrganization/CreateOrganizationForm.tsx index 0bc7f29a13d..f206a91f31d 100644 --- a/packages/ui/src/components/CreateOrganization/CreateOrganizationForm.tsx +++ b/packages/ui/src/components/CreateOrganization/CreateOrganizationForm.tsx @@ -36,6 +36,7 @@ type CreateOrganizationFormProps = { }; }; +// TODO -> Prefill form with organization creation defaults export const CreateOrganizationForm = withCardStateProvider((props: CreateOrganizationFormProps) => { const card = useCardState(); const wizard = useWizard({ onNextStep: () => card.setError(undefined) }); diff --git a/packages/ui/src/components/SessionTasks/tasks/TaskChooseOrganization/CreateOrganizationScreen.tsx b/packages/ui/src/components/SessionTasks/tasks/TaskChooseOrganization/CreateOrganizationScreen.tsx index 75d7c0baa07..35ba1a75a56 100644 --- a/packages/ui/src/components/SessionTasks/tasks/TaskChooseOrganization/CreateOrganizationScreen.tsx +++ b/packages/ui/src/components/SessionTasks/tasks/TaskChooseOrganization/CreateOrganizationScreen.tsx @@ -4,7 +4,6 @@ import type { CreateOrganizationParams } from '@clerk/shared/types'; import { useEnvironment } from '@/ui/contexts'; import { useSessionTasksContext, useTaskChooseOrganizationContext } from '@/ui/contexts/components/SessionTasks'; import { localizationKeys } from '@/ui/customizables'; -// or from '@/ui/elements' import { useCardState } from '@/ui/elements/contexts'; import { Form } from '@/ui/elements/Form'; import { FormButtonContainer } from '@/ui/elements/FormButtons'; @@ -19,7 +18,7 @@ import { OrganizationCreationDefaultsAlert } from './OrganizationCreationDefault // TODO: Replace with actual API call to OrganizationCreationDefaults.retrieve() const organizationCreationDefaults = { - creationAdvisory: { + advisory: { type: 'existing_org_with_domain' as const, severity: 'warning' as const, }, diff --git a/packages/ui/src/components/SessionTasks/tasks/TaskChooseOrganization/OrganizationCreationDefaultsAlert.tsx b/packages/ui/src/components/SessionTasks/tasks/TaskChooseOrganization/OrganizationCreationDefaultsAlert.tsx index 067f8c1b28d..eb88d8ffcb0 100644 --- a/packages/ui/src/components/SessionTasks/tasks/TaskChooseOrganization/OrganizationCreationDefaultsAlert.tsx +++ b/packages/ui/src/components/SessionTasks/tasks/TaskChooseOrganization/OrganizationCreationDefaultsAlert.tsx @@ -1,21 +1,21 @@ import type { OrganizationCreationAdvisoryType, OrganizationCreationDefaultsResource } from '@clerk/shared/types'; import { type LocalizationKey, localizationKeys } from '@/localization'; -import { Alert } from '@/ui/elements/Alert'; // or from '@/ui/elements' +import { Alert } from '@/ui/elements/Alert'; export function OrganizationCreationDefaultsAlert({ organizationCreationDefaults, }: { organizationCreationDefaults: OrganizationCreationDefaultsResource; }) { - if (!organizationCreationDefaults.creationAdvisory) { + if (!organizationCreationDefaults.advisory) { return null; } return ( ); } From 0c0b95528f591510e251f36ecb84a77dde60698e Mon Sep 17 00:00:00 2001 From: Laura Beatris <48022589+LauraBeatris@users.noreply.github.com> Date: Wed, 17 Dec 2025 14:41:05 -0300 Subject: [PATCH 05/17] Render logo field --- .../resources/OrganizationCreationDefaults.ts | 3 + .../src/types/organizationCreationDefaults.ts | 2 + .../CreateOrganizationScreen.tsx | 60 ++++++++++++++++++- 3 files changed, 63 insertions(+), 2 deletions(-) diff --git a/packages/clerk-js/src/core/resources/OrganizationCreationDefaults.ts b/packages/clerk-js/src/core/resources/OrganizationCreationDefaults.ts index f872602acb5..460b63751ed 100644 --- a/packages/clerk-js/src/core/resources/OrganizationCreationDefaults.ts +++ b/packages/clerk-js/src/core/resources/OrganizationCreationDefaults.ts @@ -16,9 +16,11 @@ export class OrganizationCreationDefaults extends BaseResource implements Organi form: { name: string; slug: string; + logo: string | null; } = { name: '', slug: '', + logo: null, }; public constructor(data: OrganizationCreationDefaultsJSON | OrganizationCreationDefaultsJSONSnapshot | null = null) { @@ -38,6 +40,7 @@ export class OrganizationCreationDefaults extends BaseResource implements Organi if (data.form) { this.form.name = this.withDefault(data.form.name, this.form.name); this.form.slug = this.withDefault(data.form.slug, this.form.slug); + this.form.logo = this.withDefault(data.form.logo, this.form.logo); } return this; diff --git a/packages/shared/src/types/organizationCreationDefaults.ts b/packages/shared/src/types/organizationCreationDefaults.ts index badc42917d0..4b3cd280f19 100644 --- a/packages/shared/src/types/organizationCreationDefaults.ts +++ b/packages/shared/src/types/organizationCreationDefaults.ts @@ -13,6 +13,7 @@ export interface OrganizationCreationDefaultsJSON extends ClerkResourceJSON { form: { name: string; slug: string; + logo: string | null; }; } @@ -24,5 +25,6 @@ export interface OrganizationCreationDefaultsResource extends ClerkResource { form: { name: string; slug: string; + logo: string | null; }; } diff --git a/packages/ui/src/components/SessionTasks/tasks/TaskChooseOrganization/CreateOrganizationScreen.tsx b/packages/ui/src/components/SessionTasks/tasks/TaskChooseOrganization/CreateOrganizationScreen.tsx index 35ba1a75a56..1c0d19fc826 100644 --- a/packages/ui/src/components/SessionTasks/tasks/TaskChooseOrganization/CreateOrganizationScreen.tsx +++ b/packages/ui/src/components/SessionTasks/tasks/TaskChooseOrganization/CreateOrganizationScreen.tsx @@ -1,27 +1,37 @@ import { useOrganizationList } from '@clerk/shared/react'; import type { CreateOrganizationParams } from '@clerk/shared/types'; +import React from 'react'; import { useEnvironment } from '@/ui/contexts'; import { useSessionTasksContext, useTaskChooseOrganizationContext } from '@/ui/contexts/components/SessionTasks'; -import { localizationKeys } from '@/ui/customizables'; +import { Icon, localizationKeys } from '@/ui/customizables'; import { useCardState } from '@/ui/elements/contexts'; import { Form } from '@/ui/elements/Form'; import { FormButtonContainer } from '@/ui/elements/FormButtons'; import { FormContainer } from '@/ui/elements/FormContainer'; import { Header } from '@/ui/elements/Header'; +import { IconButton } from '@/ui/elements/IconButton'; +import { Upload } from '@/ui/icons'; import { createSlug } from '@/ui/utils/createSlug'; import { handleError } from '@/ui/utils/errorHandler'; import { useFormControl } from '@/ui/utils/useFormControl'; +import { OrganizationProfileAvatarUploader } from '../../../OrganizationProfile/OrganizationProfileAvatarUploader'; import { organizationListParams } from '../../../OrganizationSwitcher/utils'; import { OrganizationCreationDefaultsAlert } from './OrganizationCreationDefaultsAlert'; // TODO: Replace with actual API call to OrganizationCreationDefaults.retrieve() +// TODO - Only replace if .organization_settings.organization_creation_defaults.enabled const organizationCreationDefaults = { advisory: { type: 'existing_org_with_domain' as const, severity: 'warning' as const, }, + form: { + name: '', + slug: '', + logo: null, + }, pathRoot: '', reload: () => Promise.resolve({} as any), }; @@ -38,6 +48,7 @@ export const CreateOrganizationScreen = (props: CreateOrganizationScreenProps) = userMemberships: organizationListParams.userMemberships, }); const { organizationSettings } = useEnvironment(); + const [file, setFile] = React.useState(); const nameField = useFormControl('name', '', { type: 'text', @@ -68,6 +79,10 @@ export const CreateOrganizationScreen = (props: CreateOrganizationScreenProps) = const organization = await createOrganization(createOrgParams); + if (file) { + await organization.setLogo({ file }); + } + await setActive({ organization, navigate: async ({ session }) => { @@ -88,6 +103,11 @@ export const CreateOrganizationScreen = (props: CreateOrganizationScreenProps) = slugField.setValue(val); }; + const onAvatarRemove = () => { + card.setIdle(); + return setFile(null); + }; + const isSubmitButtonDisabled = !nameField.value || !isLoaded; return ( @@ -101,8 +121,44 @@ export const CreateOrganizationScreen = (props: CreateOrganizationScreenProps) = ({ padding: `${t.space.$none} ${t.space.$10} ${t.space.$8}` })}> - + + await setFile(file)} + onAvatarRemove={file ? onAvatarRemove : null} + avatarPreviewPlaceholder={ + ({ + color: t.colors.$colorMutedForeground, + transitionDuration: t.transitionDuration.$controls, + })} + /> + } + sx={t => ({ + width: t.sizes.$16, + height: t.sizes.$16, + borderRadius: t.radii.$md, + borderWidth: t.borderWidths.$normal, + borderStyle: t.borderStyles.$dashed, + borderColor: t.colors.$borderAlpha200, + backgroundColor: t.colors.$neutralAlpha50, + ':hover': { + backgroundColor: t.colors.$neutralAlpha50, + svg: { + transform: 'scale(1.2)', + }, + }, + })} + /> + } + /> Date: Wed, 17 Dec 2025 14:51:54 -0300 Subject: [PATCH 06/17] Add `organizationCreationDefaults` to environment resource --- .../src/core/resources/OrganizationSettings.ts | 12 ++++++++++++ packages/shared/src/types/organizationSettings.ts | 6 ++++++ .../CreateOrganizationScreen.tsx | 4 ++-- 3 files changed, 20 insertions(+), 2 deletions(-) diff --git a/packages/clerk-js/src/core/resources/OrganizationSettings.ts b/packages/clerk-js/src/core/resources/OrganizationSettings.ts index 8960b347d62..a9fa5873d49 100644 --- a/packages/clerk-js/src/core/resources/OrganizationSettings.ts +++ b/packages/clerk-js/src/core/resources/OrganizationSettings.ts @@ -23,6 +23,11 @@ export class OrganizationSettings extends BaseResource implements OrganizationSe } = { disabled: false, }; + organizationCreationDefaults: { + enabled: boolean; + } = { + enabled: false, + }; enabled: boolean = false; maxAllowedMemberships: number = 1; forceOrganizationSelection!: boolean; @@ -51,6 +56,13 @@ export class OrganizationSettings extends BaseResource implements OrganizationSe this.slug.disabled = this.withDefault(data.slug.disabled, this.slug.disabled); } + if (data.organization_creation_defaults) { + this.organizationCreationDefaults.enabled = this.withDefault( + data.organization_creation_defaults.enabled, + this.organizationCreationDefaults.enabled, + ); + } + this.enabled = this.withDefault(data.enabled, this.enabled); this.maxAllowedMemberships = this.withDefault(data.max_allowed_memberships, this.maxAllowedMemberships); this.forceOrganizationSelection = this.withDefault( diff --git a/packages/shared/src/types/organizationSettings.ts b/packages/shared/src/types/organizationSettings.ts index ab9e0704e1e..e9a24b8e0f0 100644 --- a/packages/shared/src/types/organizationSettings.ts +++ b/packages/shared/src/types/organizationSettings.ts @@ -20,6 +20,9 @@ export interface OrganizationSettingsJSON extends ClerkResourceJSON { slug: { disabled: boolean; }; + organization_creation_defaults: { + enabled: boolean; + }; } export interface OrganizationSettingsResource extends ClerkResource { @@ -37,5 +40,8 @@ export interface OrganizationSettingsResource extends ClerkResource { slug: { disabled: boolean; }; + organizationCreationDefaults: { + enabled: boolean; + }; __internal_toSnapshot: () => OrganizationSettingsJSONSnapshot; } diff --git a/packages/ui/src/components/SessionTasks/tasks/TaskChooseOrganization/CreateOrganizationScreen.tsx b/packages/ui/src/components/SessionTasks/tasks/TaskChooseOrganization/CreateOrganizationScreen.tsx index 1c0d19fc826..c5e7b1a1175 100644 --- a/packages/ui/src/components/SessionTasks/tasks/TaskChooseOrganization/CreateOrganizationScreen.tsx +++ b/packages/ui/src/components/SessionTasks/tasks/TaskChooseOrganization/CreateOrganizationScreen.tsx @@ -1,6 +1,6 @@ import { useOrganizationList } from '@clerk/shared/react'; import type { CreateOrganizationParams } from '@clerk/shared/types'; -import React from 'react'; +import { useState } from 'react'; import { useEnvironment } from '@/ui/contexts'; import { useSessionTasksContext, useTaskChooseOrganizationContext } from '@/ui/contexts/components/SessionTasks'; @@ -48,7 +48,7 @@ export const CreateOrganizationScreen = (props: CreateOrganizationScreenProps) = userMemberships: organizationListParams.userMemberships, }); const { organizationSettings } = useEnvironment(); - const [file, setFile] = React.useState(); + const [file, setFile] = useState(); const nameField = useFormControl('name', '', { type: 'text', From 0aa3266947ebf3526771f5bf92f65681453e83df Mon Sep 17 00:00:00 2001 From: Laura Beatris <48022589+LauraBeatris@users.noreply.github.com> Date: Thu, 18 Dec 2025 17:22:15 -0300 Subject: [PATCH 07/17] Prefill with org defaults --- .../src/core/resources/Environment.ts | 2 ++ packages/clerk-js/src/core/resources/User.ts | 3 ++ packages/shared/src/types/user.ts | 2 ++ .../CreateOrganizationScreen.tsx | 35 +++++++------------ .../OrganizationCreationDefaultsAlert.tsx | 19 ++++++---- .../__tests__/TaskChooseOrganization.test.tsx | 10 ++++-- .../tasks/TaskChooseOrganization/index.tsx | 19 ++++++++-- packages/ui/src/elements/AvatarUploader.tsx | 3 +- 8 files changed, 59 insertions(+), 34 deletions(-) diff --git a/packages/clerk-js/src/core/resources/Environment.ts b/packages/clerk-js/src/core/resources/Environment.ts index 5e961e3f354..53a3404bc6f 100644 --- a/packages/clerk-js/src/core/resources/Environment.ts +++ b/packages/clerk-js/src/core/resources/Environment.ts @@ -16,6 +16,8 @@ import { APIKeySettings } from './APIKeySettings'; import { AuthConfig, BaseResource, CommerceSettings, DisplayConfig, ProtectConfig, UserSettings } from './internal'; import { OrganizationSettings } from './OrganizationSettings'; +// TODO -> Update with new flag for default orgs +// Use it to conditionally trigger query export class Environment extends BaseResource implements EnvironmentResource { private static instance: Environment; diff --git a/packages/clerk-js/src/core/resources/User.ts b/packages/clerk-js/src/core/resources/User.ts index ae7ef203c63..079b7ae2f75 100644 --- a/packages/clerk-js/src/core/resources/User.ts +++ b/packages/clerk-js/src/core/resources/User.ts @@ -53,6 +53,7 @@ import { UserOrganizationInvitation, Web3Wallet, } from './internal'; +import { OrganizationCreationDefaults } from './OrganizationCreationDefaults'; export class User extends BaseResource implements UserResource { pathRoot = '/me'; @@ -275,6 +276,8 @@ export class User extends BaseResource implements UserResource { getOrganizationMemberships: GetOrganizationMemberships = retrieveMembership => OrganizationMembership.retrieve(retrieveMembership); + getOrganizationCreationDefaults = () => OrganizationCreationDefaults.retrieve(); + leaveOrganization = async (organizationId: string): Promise => { const json = ( await BaseResource._fetch({ diff --git a/packages/shared/src/types/user.ts b/packages/shared/src/types/user.ts index 93d7a9ef4a4..ac1c40b2fbd 100644 --- a/packages/shared/src/types/user.ts +++ b/packages/shared/src/types/user.ts @@ -7,6 +7,7 @@ import type { ExternalAccountResource } from './externalAccount'; import type { ImageResource } from './image'; import type { UserJSON } from './json'; import type { OAuthScope } from './oauth'; +import type { OrganizationCreationDefaultsResource } from './organizationCreationDefaults'; import type { OrganizationInvitationStatus } from './organizationInvitation'; import type { OrganizationMembershipResource } from './organizationMembership'; import type { OrganizationSuggestionResource, OrganizationSuggestionStatus } from './organizationSuggestion'; @@ -115,6 +116,7 @@ export interface UserResource extends ClerkResource, BillingPayerMethods { getOrganizationSuggestions: ( params?: GetUserOrganizationSuggestionsParams, ) => Promise>; + getOrganizationCreationDefaults: () => Promise; leaveOrganization: (organizationId: string) => Promise; createTOTP: () => Promise; verifyTOTP: (params: VerifyTOTPParams) => Promise; diff --git a/packages/ui/src/components/SessionTasks/tasks/TaskChooseOrganization/CreateOrganizationScreen.tsx b/packages/ui/src/components/SessionTasks/tasks/TaskChooseOrganization/CreateOrganizationScreen.tsx index c5e7b1a1175..e0f3ee05eed 100644 --- a/packages/ui/src/components/SessionTasks/tasks/TaskChooseOrganization/CreateOrganizationScreen.tsx +++ b/packages/ui/src/components/SessionTasks/tasks/TaskChooseOrganization/CreateOrganizationScreen.tsx @@ -1,5 +1,5 @@ import { useOrganizationList } from '@clerk/shared/react'; -import type { CreateOrganizationParams } from '@clerk/shared/types'; +import type { CreateOrganizationParams, OrganizationCreationDefaultsResource } from '@clerk/shared/types'; import { useState } from 'react'; import { useEnvironment } from '@/ui/contexts'; @@ -20,24 +20,9 @@ import { OrganizationProfileAvatarUploader } from '../../../OrganizationProfile/ import { organizationListParams } from '../../../OrganizationSwitcher/utils'; import { OrganizationCreationDefaultsAlert } from './OrganizationCreationDefaultsAlert'; -// TODO: Replace with actual API call to OrganizationCreationDefaults.retrieve() -// TODO - Only replace if .organization_settings.organization_creation_defaults.enabled -const organizationCreationDefaults = { - advisory: { - type: 'existing_org_with_domain' as const, - severity: 'warning' as const, - }, - form: { - name: '', - slug: '', - logo: null, - }, - pathRoot: '', - reload: () => Promise.resolve({} as any), -}; - type CreateOrganizationScreenProps = { onCancel?: () => void; + organizationCreationDefaults?: OrganizationCreationDefaultsResource; }; export const CreateOrganizationScreen = (props: CreateOrganizationScreenProps) => { @@ -50,12 +35,12 @@ export const CreateOrganizationScreen = (props: CreateOrganizationScreenProps) = const { organizationSettings } = useEnvironment(); const [file, setFile] = useState(); - const nameField = useFormControl('name', '', { + const nameField = useFormControl('name', props.organizationCreationDefaults?.form.name ?? '', { type: 'text', label: localizationKeys('taskChooseOrganization.createOrganization.formFieldLabel__name'), placeholder: localizationKeys('taskChooseOrganization.createOrganization.formFieldInputPlaceholder__name'), }); - const slugField = useFormControl('slug', '', { + const slugField = useFormControl('slug', props.organizationCreationDefaults?.form.slug ?? '', { type: 'text', label: localizationKeys('taskChooseOrganization.createOrganization.formFieldLabel__slug'), placeholder: localizationKeys('taskChooseOrganization.createOrganization.formFieldInputPlaceholder__slug'), @@ -81,6 +66,11 @@ export const CreateOrganizationScreen = (props: CreateOrganizationScreenProps) = if (file) { await organization.setLogo({ file }); + } else if (defaultLogoUrl) { + const response = await fetch(defaultLogoUrl); + const blob = await response.blob(); + const logoFile = new File([blob], 'logo', { type: blob.type }); + await organization.setLogo({ file: logoFile }); } await setActive({ @@ -109,6 +99,7 @@ export const CreateOrganizationScreen = (props: CreateOrganizationScreenProps) = }; const isSubmitButtonDisabled = !nameField.value || !isLoaded; + const defaultLogoUrl = file === undefined ? props.organizationCreationDefaults?.form.logo : undefined; return ( <> @@ -122,11 +113,11 @@ export const CreateOrganizationScreen = (props: CreateOrganizationScreenProps) = ({ padding: `${t.space.$none} ${t.space.$10} ${t.space.$8}` })}> - + await setFile(file)} - onAvatarRemove={file ? onAvatarRemove : null} + onAvatarRemove={file || defaultLogoUrl ? onAvatarRemove : null} avatarPreviewPlaceholder={ + + + ); } +// TODO -> Update with latest advisory where meta is returned +// TODO -> Include email domain in message, eg: {{ meta }} const advisoryTypeToLocalizationKey: Record = { existing_org_with_domain: localizationKeys('taskChooseOrganization.alerts.existingOrgWithDomain'), }; diff --git a/packages/ui/src/components/SessionTasks/tasks/TaskChooseOrganization/__tests__/TaskChooseOrganization.test.tsx b/packages/ui/src/components/SessionTasks/tasks/TaskChooseOrganization/__tests__/TaskChooseOrganization.test.tsx index 987235c642b..ec40cf1b8fd 100644 --- a/packages/ui/src/components/SessionTasks/tasks/TaskChooseOrganization/__tests__/TaskChooseOrganization.test.tsx +++ b/packages/ui/src/components/SessionTasks/tasks/TaskChooseOrganization/__tests__/TaskChooseOrganization.test.tsx @@ -355,8 +355,14 @@ describe('TaskChooseOrganization', () => { }); describe('with organization creation defaults', () => { - it.todo('displays warning when organization already exists for user email domain'); + describe('when enabled on environment', () => { + it.todo('displays warning when organization already exists for user email domain'); - it.todo('prefills create organization form with defaults'); + it.todo('prefills create organization form with defaults'); + }); + + describe('when disabled on environment', () => { + it.todo('does not fetch for creation defaults'); + }); }); }); diff --git a/packages/ui/src/components/SessionTasks/tasks/TaskChooseOrganization/index.tsx b/packages/ui/src/components/SessionTasks/tasks/TaskChooseOrganization/index.tsx index dc3375e0d0b..eac9b229241 100644 --- a/packages/ui/src/components/SessionTasks/tasks/TaskChooseOrganization/index.tsx +++ b/packages/ui/src/components/SessionTasks/tasks/TaskChooseOrganization/index.tsx @@ -1,7 +1,9 @@ import { useClerk, useSession, useUser } from '@clerk/shared/react'; +import type { OrganizationCreationDefaultsResource } from '@clerk/shared/types'; import { useState } from 'react'; -import { useSignOutContext, withCoreSessionSwitchGuard } from '@/ui/contexts'; +import { useFetch } from '@/hooks'; +import { useEnvironment, useSignOutContext, withCoreSessionSwitchGuard } from '@/ui/contexts'; import { descriptors, Flex, Flow, localizationKeys, Spinner } from '@/ui/customizables'; import { Card } from '@/ui/elements/Card'; import { withCardStateProvider } from '@/ui/elements/contexts'; @@ -16,6 +18,14 @@ import { CreateOrganizationScreen } from './CreateOrganizationScreen'; const TaskChooseOrganizationInternal = () => { const { user } = useUser(); const { userMemberships, userSuggestions, userInvitations } = useOrganizationListInView(); + const { environment } = useEnvironment(); + const organizationCreationDefaults = useFetch( + environment.organizationSettings.organizationCreationDefaults.enabled + ? user?.getOrganizationCreationDefaults + : undefined, + 'organization-creation-defaults', + { staleTime: Infinity }, + ); const isLoading = userMemberships?.isLoading || userInvitations?.isLoading || userSuggestions?.isLoading; const hasExistingResources = !!(userMemberships?.count || userInvitations?.count || userSuggestions?.count); @@ -47,7 +57,10 @@ const TaskChooseOrganizationInternal = () => { /> ) : ( - + )} @@ -103,6 +116,7 @@ const TaskChooseOrganizationCardFooter = () => { type TaskChooseOrganizationFlowsProps = { initialFlow: 'create' | 'choose'; + organizationCreationDefaults?: OrganizationCreationDefaultsResource; }; const TaskChooseOrganizationFlows = withCardStateProvider((props: TaskChooseOrganizationFlowsProps) => { @@ -112,6 +126,7 @@ const TaskChooseOrganizationFlows = withCardStateProvider((props: TaskChooseOrga return ( setCurrentFlow('choose') : undefined} + organizationCreationDefaults={props.organizationCreationDefaults} /> ); } diff --git a/packages/ui/src/elements/AvatarUploader.tsx b/packages/ui/src/elements/AvatarUploader.tsx index f1ae367f2c2..8a7f99b7a0d 100644 --- a/packages/ui/src/elements/AvatarUploader.tsx +++ b/packages/ui/src/elements/AvatarUploader.tsx @@ -90,9 +90,10 @@ export const AvatarUploader = (props: AvatarUploaderProps) => { await handleFileDrop(f); }; + const hasExistingImage = !!(avatarPreview.props as { imageUrl?: string })?.imageUrl; const previewElement = objectUrl ? React.cloneElement(avatarPreview, { imageUrl: objectUrl }) - : avatarPreviewPlaceholder + : avatarPreviewPlaceholder && !hasExistingImage ? React.cloneElement(avatarPreviewPlaceholder, { onClick: openDialog }) : avatarPreview; From f6b80b9d91589d4c5bdec080f648e2cb1569a7a8 Mon Sep 17 00:00:00 2001 From: Laura Beatris <48022589+LauraBeatris@users.noreply.github.com> Date: Fri, 19 Dec 2025 12:03:41 -0300 Subject: [PATCH 08/17] Conditionally fetch for defaults --- packages/clerk-js/src/core/resources/Environment.ts | 2 -- .../CreateOrganization/CreateOrganizationForm.tsx | 1 - .../SessionTasks/tasks/TaskChooseOrganization/index.tsx | 6 ++---- 3 files changed, 2 insertions(+), 7 deletions(-) diff --git a/packages/clerk-js/src/core/resources/Environment.ts b/packages/clerk-js/src/core/resources/Environment.ts index 53a3404bc6f..5e961e3f354 100644 --- a/packages/clerk-js/src/core/resources/Environment.ts +++ b/packages/clerk-js/src/core/resources/Environment.ts @@ -16,8 +16,6 @@ import { APIKeySettings } from './APIKeySettings'; import { AuthConfig, BaseResource, CommerceSettings, DisplayConfig, ProtectConfig, UserSettings } from './internal'; import { OrganizationSettings } from './OrganizationSettings'; -// TODO -> Update with new flag for default orgs -// Use it to conditionally trigger query export class Environment extends BaseResource implements EnvironmentResource { private static instance: Environment; diff --git a/packages/ui/src/components/CreateOrganization/CreateOrganizationForm.tsx b/packages/ui/src/components/CreateOrganization/CreateOrganizationForm.tsx index f206a91f31d..0bc7f29a13d 100644 --- a/packages/ui/src/components/CreateOrganization/CreateOrganizationForm.tsx +++ b/packages/ui/src/components/CreateOrganization/CreateOrganizationForm.tsx @@ -36,7 +36,6 @@ type CreateOrganizationFormProps = { }; }; -// TODO -> Prefill form with organization creation defaults export const CreateOrganizationForm = withCardStateProvider((props: CreateOrganizationFormProps) => { const card = useCardState(); const wizard = useWizard({ onNextStep: () => card.setError(undefined) }); diff --git a/packages/ui/src/components/SessionTasks/tasks/TaskChooseOrganization/index.tsx b/packages/ui/src/components/SessionTasks/tasks/TaskChooseOrganization/index.tsx index eac9b229241..ef33b51c3b5 100644 --- a/packages/ui/src/components/SessionTasks/tasks/TaskChooseOrganization/index.tsx +++ b/packages/ui/src/components/SessionTasks/tasks/TaskChooseOrganization/index.tsx @@ -18,11 +18,9 @@ import { CreateOrganizationScreen } from './CreateOrganizationScreen'; const TaskChooseOrganizationInternal = () => { const { user } = useUser(); const { userMemberships, userSuggestions, userInvitations } = useOrganizationListInView(); - const { environment } = useEnvironment(); + const { organizationSettings } = useEnvironment(); const organizationCreationDefaults = useFetch( - environment.organizationSettings.organizationCreationDefaults.enabled - ? user?.getOrganizationCreationDefaults - : undefined, + organizationSettings.organizationCreationDefaults?.enabled ? user?.getOrganizationCreationDefaults : undefined, 'organization-creation-defaults', { staleTime: Infinity }, ); From b420efebfadee6b06bdae0b075d3e03aa7725b2d Mon Sep 17 00:00:00 2001 From: Laura Beatris <48022589+LauraBeatris@users.noreply.github.com> Date: Fri, 19 Dec 2025 12:12:19 -0300 Subject: [PATCH 09/17] Include meta into advisory message --- .../resources/OrganizationCreationDefaults.ts | 6 +++-- packages/localizations/src/en-US.ts | 2 +- packages/shared/src/types/localization.ts | 2 +- .../src/types/organizationCreationDefaults.ts | 6 +++-- .../OrganizationCreationDefaultsAlert.tsx | 26 +++++++++++++------ .../tasks/TaskChooseOrganization/index.tsx | 2 +- 6 files changed, 29 insertions(+), 15 deletions(-) diff --git a/packages/clerk-js/src/core/resources/OrganizationCreationDefaults.ts b/packages/clerk-js/src/core/resources/OrganizationCreationDefaults.ts index 460b63751ed..865d9085f3a 100644 --- a/packages/clerk-js/src/core/resources/OrganizationCreationDefaults.ts +++ b/packages/clerk-js/src/core/resources/OrganizationCreationDefaults.ts @@ -10,8 +10,9 @@ import { BaseResource } from './internal'; export class OrganizationCreationDefaults extends BaseResource implements OrganizationCreationDefaultsResource { advisory: { - type: OrganizationCreationAdvisoryType; + code: OrganizationCreationAdvisoryType; severity: OrganizationCreationAdvisorySeverity; + meta: Record; } | null = null; form: { name: string; @@ -60,7 +61,8 @@ export class OrganizationCreationDefaults extends BaseResource implements Organi return { advisory: this.advisory ? { - type: this.advisory.type, + code: this.advisory.code, + meta: this.advisory.meta, severity: this.advisory.severity, } : null, diff --git a/packages/localizations/src/en-US.ts b/packages/localizations/src/en-US.ts index 9eb252dafb2..5c2d1c27319 100644 --- a/packages/localizations/src/en-US.ts +++ b/packages/localizations/src/en-US.ts @@ -886,7 +886,7 @@ export const enUS: LocalizationResource = { }, alerts: { existingOrgWithDomain: - 'An organization already exists for the detected company name and email domain. Join by invitation.', + 'An organization already exists for the detected company name and {{email}}. Join by invitation.', }, }, taskResetPassword: { diff --git a/packages/shared/src/types/localization.ts b/packages/shared/src/types/localization.ts index 7b5e19d33df..18d9f439ed0 100644 --- a/packages/shared/src/types/localization.ts +++ b/packages/shared/src/types/localization.ts @@ -1315,7 +1315,7 @@ export type __internal_LocalizationResource = { subtitle: LocalizationValue; }; alerts: { - existingOrgWithDomain: LocalizationValue; + existingOrgWithDomain: LocalizationValue<'email'>; }; }; taskResetPassword: { diff --git a/packages/shared/src/types/organizationCreationDefaults.ts b/packages/shared/src/types/organizationCreationDefaults.ts index 4b3cd280f19..5c3fdb2a24d 100644 --- a/packages/shared/src/types/organizationCreationDefaults.ts +++ b/packages/shared/src/types/organizationCreationDefaults.ts @@ -7,8 +7,9 @@ export type OrganizationCreationAdvisorySeverity = 'warning'; export interface OrganizationCreationDefaultsJSON extends ClerkResourceJSON { advisory: { - type: OrganizationCreationAdvisoryType; + code: OrganizationCreationAdvisoryType; severity: OrganizationCreationAdvisorySeverity; + meta: Record; } | null; form: { name: string; @@ -19,8 +20,9 @@ export interface OrganizationCreationDefaultsJSON extends ClerkResourceJSON { export interface OrganizationCreationDefaultsResource extends ClerkResource { advisory: { - type: OrganizationCreationAdvisoryType; + code: OrganizationCreationAdvisoryType; severity: OrganizationCreationAdvisorySeverity; + meta: Record; } | null; form: { name: string; diff --git a/packages/ui/src/components/SessionTasks/tasks/TaskChooseOrganization/OrganizationCreationDefaultsAlert.tsx b/packages/ui/src/components/SessionTasks/tasks/TaskChooseOrganization/OrganizationCreationDefaultsAlert.tsx index 9e15365aaf0..7cca5d332ba 100644 --- a/packages/ui/src/components/SessionTasks/tasks/TaskChooseOrganization/OrganizationCreationDefaultsAlert.tsx +++ b/packages/ui/src/components/SessionTasks/tasks/TaskChooseOrganization/OrganizationCreationDefaultsAlert.tsx @@ -1,14 +1,15 @@ -import type { OrganizationCreationAdvisoryType, OrganizationCreationDefaultsResource } from '@clerk/shared/types'; +import type { OrganizationCreationDefaultsResource } from '@clerk/shared/types'; import { Alert, Text } from '@/customizables'; -import { type LocalizationKey, localizationKeys } from '@/localization'; +import { localizationKeys } from '@/localization'; export function OrganizationCreationDefaultsAlert({ organizationCreationDefaults, }: { organizationCreationDefaults?: OrganizationCreationDefaultsResource; }) { - if (!organizationCreationDefaults?.advisory) { + const localizationKey = advisoryToLocalizationKey(organizationCreationDefaults?.advisory); + if (!localizationKey) { return null; } @@ -16,15 +17,24 @@ export function OrganizationCreationDefaultsAlert({ ); } -// TODO -> Update with latest advisory where meta is returned -// TODO -> Include email domain in message, eg: {{ meta }} -const advisoryTypeToLocalizationKey: Record = { - existing_org_with_domain: localizationKeys('taskChooseOrganization.alerts.existingOrgWithDomain'), +const advisoryToLocalizationKey = (advisory?: OrganizationCreationDefaultsResource['advisory']) => { + if (!advisory) { + return null; + } + + switch (advisory.code) { + case 'existing_org_with_domain': + return localizationKeys('taskChooseOrganization.alerts.existingOrgWithDomain', { + email: advisory.meta.email, + }); + default: + return null; + } }; diff --git a/packages/ui/src/components/SessionTasks/tasks/TaskChooseOrganization/index.tsx b/packages/ui/src/components/SessionTasks/tasks/TaskChooseOrganization/index.tsx index ef33b51c3b5..e2310987b3f 100644 --- a/packages/ui/src/components/SessionTasks/tasks/TaskChooseOrganization/index.tsx +++ b/packages/ui/src/components/SessionTasks/tasks/TaskChooseOrganization/index.tsx @@ -114,7 +114,7 @@ const TaskChooseOrganizationCardFooter = () => { type TaskChooseOrganizationFlowsProps = { initialFlow: 'create' | 'choose'; - organizationCreationDefaults?: OrganizationCreationDefaultsResource; + organizationCreationDefaults?: OrganizationCreationDefaultsResource | null; }; const TaskChooseOrganizationFlows = withCardStateProvider((props: TaskChooseOrganizationFlowsProps) => { From 8a0e4698cc8a9c9001dc9ed549cc66f56cc178bd Mon Sep 17 00:00:00 2001 From: Laura Beatris <48022589+LauraBeatris@users.noreply.github.com> Date: Fri, 19 Dec 2025 12:25:24 -0300 Subject: [PATCH 10/17] Implement tests --- packages/clerk-js/src/test/fixture-helpers.ts | 4 + .../__tests__/TaskChooseOrganization.test.tsx | 74 ++++++++++++++++++- .../tasks/TaskChooseOrganization/index.tsx | 1 - 3 files changed, 75 insertions(+), 4 deletions(-) diff --git a/packages/clerk-js/src/test/fixture-helpers.ts b/packages/clerk-js/src/test/fixture-helpers.ts index 86547dae2c0..b1564bcd841 100644 --- a/packages/clerk-js/src/test/fixture-helpers.ts +++ b/packages/clerk-js/src/test/fixture-helpers.ts @@ -344,6 +344,9 @@ const createOrganizationSettingsFixtureHelpers = (environment: EnvironmentJSON) const withOrganizationSlug = (enabled = false) => { os.slug.disabled = !enabled; }; + const withOrganizationCreationDefaults = (enabled = false) => { + os.organization_creation_defaults.enabled = enabled; + }; const withOrganizationDomains = (modes?: OrganizationEnrollmentMode[], defaultRole?: string) => { os.domains.enabled = true; @@ -356,6 +359,7 @@ const createOrganizationSettingsFixtureHelpers = (environment: EnvironmentJSON) withOrganizationDomains, withForceOrganizationSelection, withOrganizationSlug, + withOrganizationCreationDefaults, }; }; diff --git a/packages/ui/src/components/SessionTasks/tasks/TaskChooseOrganization/__tests__/TaskChooseOrganization.test.tsx b/packages/ui/src/components/SessionTasks/tasks/TaskChooseOrganization/__tests__/TaskChooseOrganization.test.tsx index ec40cf1b8fd..0af877bdb58 100644 --- a/packages/ui/src/components/SessionTasks/tasks/TaskChooseOrganization/__tests__/TaskChooseOrganization.test.tsx +++ b/packages/ui/src/components/SessionTasks/tasks/TaskChooseOrganization/__tests__/TaskChooseOrganization.test.tsx @@ -356,13 +356,81 @@ describe('TaskChooseOrganization', () => { describe('with organization creation defaults', () => { describe('when enabled on environment', () => { - it.todo('displays warning when organization already exists for user email domain'); + it('displays warning when organization already exists for user email domain', async () => { + const { wrapper, fixtures } = await createFixtures(f => { + f.withOrganizations(); + f.withForceOrganizationSelection(); + f.withOrganizationCreationDefaults(true); + f.withUser({ + email_addresses: ['test@clerk.com'], + create_organization_enabled: true, + tasks: [{ key: 'choose-organization' }], + }); + }); + + fixtures.clerk.user?.getOrganizationCreationDefaults.mockReturnValueOnce( + Promise.resolve({ + advisory: { + code: 'existing_org_with_domain', + severity: 'warning', + meta: { email: 'test@clerk.com' }, + }, + }), + ); + + const { findByText } = render(, { wrapper }); + + expect( + await findByText(/an organization already exists for the detected company name and test@clerk\.com/i), + ).toBeInTheDocument(); + }); + + it('prefills create organization form with defaults', async () => { + const { wrapper, fixtures } = await createFixtures(f => { + f.withOrganizations(); + f.withForceOrganizationSelection(); + f.withOrganizationCreationDefaults(true); + f.withUser({ + email_addresses: ['test@clerk.com'], + create_organization_enabled: true, + tasks: [{ key: 'choose-organization' }], + }); + }); - it.todo('prefills create organization form with defaults'); + fixtures.clerk.user?.getOrganizationCreationDefaults.mockReturnValueOnce( + Promise.resolve({ + form: { + name: 'Test Org', + slug: 'test-org', + logo: null, + }, + }), + ); + + const { findByText } = render(, { wrapper }); + + expect(await findByText('Test Org')).toBeInTheDocument(); + expect(await findByText('test-org')).toBeInTheDocument(); + }); }); describe('when disabled on environment', () => { - it.todo('does not fetch for creation defaults'); + it('does not fetch for creation defaults', async () => { + const { wrapper, fixtures } = await createFixtures(f => { + f.withOrganizations(); + f.withForceOrganizationSelection(); + f.withOrganizationCreationDefaults(false); + f.withUser({ + email_addresses: ['test@clerk.com'], + create_organization_enabled: true, + tasks: [{ key: 'choose-organization' }], + }); + }); + + render(, { wrapper }); + + expect(fixtures.clerk.user?.getOrganizationCreationDefaults).not.toHaveBeenCalled(); + }); }); }); }); diff --git a/packages/ui/src/components/SessionTasks/tasks/TaskChooseOrganization/index.tsx b/packages/ui/src/components/SessionTasks/tasks/TaskChooseOrganization/index.tsx index e2310987b3f..7913f736626 100644 --- a/packages/ui/src/components/SessionTasks/tasks/TaskChooseOrganization/index.tsx +++ b/packages/ui/src/components/SessionTasks/tasks/TaskChooseOrganization/index.tsx @@ -22,7 +22,6 @@ const TaskChooseOrganizationInternal = () => { const organizationCreationDefaults = useFetch( organizationSettings.organizationCreationDefaults?.enabled ? user?.getOrganizationCreationDefaults : undefined, 'organization-creation-defaults', - { staleTime: Infinity }, ); const isLoading = userMemberships?.isLoading || userInvitations?.isLoading || userSuggestions?.isLoading; From 6d3189d283392a900473bb24bf2ffcc222b7c555 Mon Sep 17 00:00:00 2001 From: Laura Beatris <48022589+LauraBeatris@users.noreply.github.com> Date: Fri, 19 Dec 2025 12:47:13 -0300 Subject: [PATCH 11/17] Add changeset --- .changeset/calm-maps-work.md | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 .changeset/calm-maps-work.md diff --git a/.changeset/calm-maps-work.md b/.changeset/calm-maps-work.md new file mode 100644 index 00000000000..9583e7cb076 --- /dev/null +++ b/.changeset/calm-maps-work.md @@ -0,0 +1,8 @@ +--- +'@clerk/localizations': minor +'@clerk/clerk-js': minor +'@clerk/shared': minor +'@clerk/ui': minor +--- + +Surface organization creation defaults with prefilled form fields and advisory warnings From 51300f10ef6a787b1315ba94475cfdda5ed5cc54 Mon Sep 17 00:00:00 2001 From: Laura Beatris <48022589+LauraBeatris@users.noreply.github.com> Date: Fri, 19 Dec 2025 16:14:22 -0300 Subject: [PATCH 12/17] Finish integrating with advisory --- packages/localizations/src/en-US.ts | 2 +- packages/shared/src/types/localization.ts | 2 +- packages/shared/src/types/organizationCreationDefaults.ts | 2 +- .../OrganizationCreationDefaultsAlert.tsx | 6 +++--- .../__tests__/TaskChooseOrganization.test.tsx | 2 +- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/packages/localizations/src/en-US.ts b/packages/localizations/src/en-US.ts index 5c2d1c27319..6a9f5044bdc 100644 --- a/packages/localizations/src/en-US.ts +++ b/packages/localizations/src/en-US.ts @@ -885,7 +885,7 @@ export const enUS: LocalizationResource = { actionText: 'Signed in as {{identifier}}', }, alerts: { - existingOrgWithDomain: + organizationAlreadyExists: 'An organization already exists for the detected company name and {{email}}. Join by invitation.', }, }, diff --git a/packages/shared/src/types/localization.ts b/packages/shared/src/types/localization.ts index 18d9f439ed0..2cab1eab393 100644 --- a/packages/shared/src/types/localization.ts +++ b/packages/shared/src/types/localization.ts @@ -1315,7 +1315,7 @@ export type __internal_LocalizationResource = { subtitle: LocalizationValue; }; alerts: { - existingOrgWithDomain: LocalizationValue<'email'>; + organizationAlreadyExists: LocalizationValue<'email'>; }; }; taskResetPassword: { diff --git a/packages/shared/src/types/organizationCreationDefaults.ts b/packages/shared/src/types/organizationCreationDefaults.ts index 5c3fdb2a24d..d5db52f1782 100644 --- a/packages/shared/src/types/organizationCreationDefaults.ts +++ b/packages/shared/src/types/organizationCreationDefaults.ts @@ -1,7 +1,7 @@ import type { ClerkResourceJSON } from './json'; import type { ClerkResource } from './resource'; -export type OrganizationCreationAdvisoryType = 'existing_org_with_domain'; +export type OrganizationCreationAdvisoryType = 'organization_already_exists'; export type OrganizationCreationAdvisorySeverity = 'warning'; diff --git a/packages/ui/src/components/SessionTasks/tasks/TaskChooseOrganization/OrganizationCreationDefaultsAlert.tsx b/packages/ui/src/components/SessionTasks/tasks/TaskChooseOrganization/OrganizationCreationDefaultsAlert.tsx index 7cca5d332ba..ed8192e5639 100644 --- a/packages/ui/src/components/SessionTasks/tasks/TaskChooseOrganization/OrganizationCreationDefaultsAlert.tsx +++ b/packages/ui/src/components/SessionTasks/tasks/TaskChooseOrganization/OrganizationCreationDefaultsAlert.tsx @@ -30,9 +30,9 @@ const advisoryToLocalizationKey = (advisory?: OrganizationCreationDefaultsResour } switch (advisory.code) { - case 'existing_org_with_domain': - return localizationKeys('taskChooseOrganization.alerts.existingOrgWithDomain', { - email: advisory.meta.email, + case 'organization_already_exists': + return localizationKeys('taskChooseOrganization.alerts.organizationAlreadyExists', { + email: advisory.meta.organization_domain, }); default: return null; diff --git a/packages/ui/src/components/SessionTasks/tasks/TaskChooseOrganization/__tests__/TaskChooseOrganization.test.tsx b/packages/ui/src/components/SessionTasks/tasks/TaskChooseOrganization/__tests__/TaskChooseOrganization.test.tsx index 0af877bdb58..624f39c8fe5 100644 --- a/packages/ui/src/components/SessionTasks/tasks/TaskChooseOrganization/__tests__/TaskChooseOrganization.test.tsx +++ b/packages/ui/src/components/SessionTasks/tasks/TaskChooseOrganization/__tests__/TaskChooseOrganization.test.tsx @@ -371,7 +371,7 @@ describe('TaskChooseOrganization', () => { fixtures.clerk.user?.getOrganizationCreationDefaults.mockReturnValueOnce( Promise.resolve({ advisory: { - code: 'existing_org_with_domain', + code: 'organization_already_exists', severity: 'warning', meta: { email: 'test@clerk.com' }, }, From 6b2fd4056aec6fada0af6b02898dc7c28f3fffdc Mon Sep 17 00:00:00 2001 From: Laura Beatris <48022589+LauraBeatris@users.noreply.github.com> Date: Wed, 7 Jan 2026 11:30:00 -0300 Subject: [PATCH 13/17] Use loading spinner for loading status --- .../resources/OrganizationCreationDefaults.ts | 3 ++ .../src/types/organizationCreationDefaults.ts | 2 + .../OrganizationProfileAvatarUploader.tsx | 9 +++- .../CreateOrganizationScreen.tsx | 1 + packages/ui/src/elements/Avatar.tsx | 41 ++++++++++++++++++- .../ui/src/elements/OrganizationAvatar.tsx | 8 +++- 6 files changed, 58 insertions(+), 6 deletions(-) diff --git a/packages/clerk-js/src/core/resources/OrganizationCreationDefaults.ts b/packages/clerk-js/src/core/resources/OrganizationCreationDefaults.ts index 865d9085f3a..cd4b8afb45a 100644 --- a/packages/clerk-js/src/core/resources/OrganizationCreationDefaults.ts +++ b/packages/clerk-js/src/core/resources/OrganizationCreationDefaults.ts @@ -18,10 +18,12 @@ export class OrganizationCreationDefaults extends BaseResource implements Organi name: string; slug: string; logo: string | null; + blurHash: string | null; } = { name: '', slug: '', logo: null, + blurHash: null, }; public constructor(data: OrganizationCreationDefaultsJSON | OrganizationCreationDefaultsJSONSnapshot | null = null) { @@ -42,6 +44,7 @@ export class OrganizationCreationDefaults extends BaseResource implements Organi this.form.name = this.withDefault(data.form.name, this.form.name); this.form.slug = this.withDefault(data.form.slug, this.form.slug); this.form.logo = this.withDefault(data.form.logo, this.form.logo); + this.form.blurHash = this.withDefault(data.form.blur_hash, this.form.blurHash); } return this; diff --git a/packages/shared/src/types/organizationCreationDefaults.ts b/packages/shared/src/types/organizationCreationDefaults.ts index d5db52f1782..95d3211110a 100644 --- a/packages/shared/src/types/organizationCreationDefaults.ts +++ b/packages/shared/src/types/organizationCreationDefaults.ts @@ -15,6 +15,7 @@ export interface OrganizationCreationDefaultsJSON extends ClerkResourceJSON { name: string; slug: string; logo: string | null; + blur_hash: string | null; }; } @@ -28,5 +29,6 @@ export interface OrganizationCreationDefaultsResource extends ClerkResource { name: string; slug: string; logo: string | null; + blurHash: string | null; }; } diff --git a/packages/ui/src/components/OrganizationProfile/OrganizationProfileAvatarUploader.tsx b/packages/ui/src/components/OrganizationProfile/OrganizationProfileAvatarUploader.tsx index 6c146a20312..fa2c5133528 100644 --- a/packages/ui/src/components/OrganizationProfile/OrganizationProfileAvatarUploader.tsx +++ b/packages/ui/src/components/OrganizationProfile/OrganizationProfileAvatarUploader.tsx @@ -8,9 +8,13 @@ import { Col, descriptors, Text } from '../../customizables'; import { localizationKeys } from '../../localization'; export const OrganizationProfileAvatarUploader = ( - props: Omit & { organization: Partial }, + props: Omit & { + organization: Partial; + /** Shows a loading spinner while the image is loading */ + showLoadingSpinner?: boolean; + }, ) => { - const { organization, ...rest } = props; + const { organization, showLoadingSpinner, ...rest } = props; return ( @@ -28,6 +32,7 @@ export const OrganizationProfileAvatarUploader = ( avatarPreview={ theme.sizes.$16} + showLoadingSpinner={showLoadingSpinner} {...organization} /> } diff --git a/packages/ui/src/components/SessionTasks/tasks/TaskChooseOrganization/CreateOrganizationScreen.tsx b/packages/ui/src/components/SessionTasks/tasks/TaskChooseOrganization/CreateOrganizationScreen.tsx index e0f3ee05eed..f2a8106f967 100644 --- a/packages/ui/src/components/SessionTasks/tasks/TaskChooseOrganization/CreateOrganizationScreen.tsx +++ b/packages/ui/src/components/SessionTasks/tasks/TaskChooseOrganization/CreateOrganizationScreen.tsx @@ -118,6 +118,7 @@ export const CreateOrganizationScreen = (props: CreateOrganizationScreenProps) = organization={{ name: nameField.value, imageUrl: defaultLogoUrl ?? undefined }} onAvatarChange={async file => await setFile(file)} onAvatarRemove={file || defaultLogoUrl ? onAvatarRemove : null} + showLoadingSpinner={!!defaultLogoUrl} avatarPreviewPlaceholder={ & { rounded?: boolean; boxElementDescriptor?: ElementDescriptor; imageElementDescriptor?: ElementDescriptor; + /** Shows a loading spinner while the image is loading */ + showLoadingSpinner?: boolean; }; export const Avatar = (props: AvatarProps) => { @@ -28,8 +30,18 @@ export const Avatar = (props: AvatarProps) => { sx, boxElementDescriptor, imageElementDescriptor, + showLoadingSpinner = false, } = props; const [error, setError] = React.useState(false); + const [loaded, setLoaded] = React.useState(false); + + // Reset loaded state when imageUrl changes + React.useEffect(() => { + setLoaded(false); + setError(false); + }, [imageUrl]); + + const isLoading = showLoadingSpinner && imageUrl && !loaded && !error; const ImgOrFallback = initials && (!imageUrl || error) ? ( @@ -40,8 +52,15 @@ export const Avatar = (props: AvatarProps) => { title={title} alt={`${title}'s logo`} src={imageUrl || ''} - sx={{ objectFit: 'cover', width: '100%', height: '100%' }} + sx={{ + objectFit: 'cover', + width: '100%', + height: '100%', + opacity: showLoadingSpinner ? (loaded ? 1 : 0) : 1, + transition: 'opacity 0.2s ease-in-out', + }} onError={() => setError(true)} + onLoad={() => setLoaded(true)} size={imageFetchSize} /> ); @@ -67,6 +86,24 @@ export const Avatar = (props: AvatarProps) => { > {ImgOrFallback} + {isLoading && ( + ({ + position: 'absolute', + inset: 0, + alignItems: 'center', + justifyContent: 'center', + backgroundColor: t.colors.$avatarBackground, + })} + > + + + )} + {/* /** * This Box is the "shimmer" effect for the avatar. * The ":after" selector is responsible for the border shimmer animation. diff --git a/packages/ui/src/elements/OrganizationAvatar.tsx b/packages/ui/src/elements/OrganizationAvatar.tsx index 7f449455955..ad7a078f664 100644 --- a/packages/ui/src/elements/OrganizationAvatar.tsx +++ b/packages/ui/src/elements/OrganizationAvatar.tsx @@ -4,15 +4,19 @@ import type { PropsOfComponent } from '../styledSystem'; import { Avatar } from './Avatar'; type OrganizationAvatarProps = PropsOfComponent & - Partial>; + Partial> & { + /** Shows a loading spinner while the image is loading */ + showLoadingSpinner?: boolean; + }; export const OrganizationAvatar = (props: OrganizationAvatarProps) => { - const { name = '', imageUrl, ...rest } = props; + const { name = '', imageUrl, showLoadingSpinner, ...rest } = props; return ( From 87d56eadceda2d73306a39da7032fdd60d898d32 Mon Sep 17 00:00:00 2001 From: Laura Beatris <48022589+LauraBeatris@users.noreply.github.com> Date: Wed, 7 Jan 2026 17:56:34 -0300 Subject: [PATCH 14/17] Add delay to trigger loading spinner --- packages/ui/src/elements/Avatar.tsx | 51 +++++++++++++++++++++++++++-- 1 file changed, 48 insertions(+), 3 deletions(-) diff --git a/packages/ui/src/elements/Avatar.tsx b/packages/ui/src/elements/Avatar.tsx index 6234c58d018..958df6b1eb2 100644 --- a/packages/ui/src/elements/Avatar.tsx +++ b/packages/ui/src/elements/Avatar.tsx @@ -19,6 +19,9 @@ type AvatarProps = PropsOfComponent & { showLoadingSpinner?: boolean; }; +const SPINNER_DELAY_MS = 150; +const SPINNER_MIN_DURATION_MS = 400; + export const Avatar = (props: AvatarProps) => { const { size = () => 26, @@ -34,14 +37,56 @@ export const Avatar = (props: AvatarProps) => { } = props; const [error, setError] = React.useState(false); const [loaded, setLoaded] = React.useState(false); + const [spinnerVisible, setSpinnerVisible] = React.useState(false); + const spinnerShownAtRef = React.useRef(null); + const loadTimerRef = React.useRef | null>(null); - // Reset loaded state when imageUrl changes React.useEffect(() => { setLoaded(false); setError(false); + setSpinnerVisible(false); + spinnerShownAtRef.current = null; + + return () => { + if (loadTimerRef.current) { + clearTimeout(loadTimerRef.current); + loadTimerRef.current = null; + } + }; }, [imageUrl]); - const isLoading = showLoadingSpinner && imageUrl && !loaded && !error; + React.useEffect(() => { + if (!showLoadingSpinner || !imageUrl || loaded || error) { + return; + } + + const timer = setTimeout(() => { + setSpinnerVisible(true); + spinnerShownAtRef.current = Date.now(); + }, SPINNER_DELAY_MS); + + return () => clearTimeout(timer); + }, [showLoadingSpinner, imageUrl, loaded, error]); + + /** + * Prevents the loading spinner from appearing and disappearing too quickly + */ + const handleImageLoad = React.useCallback(() => { + if (spinnerShownAtRef.current) { + const elapsed = Date.now() - spinnerShownAtRef.current; + const remaining = SPINNER_MIN_DURATION_MS - elapsed; + if (remaining > 0) { + loadTimerRef.current = setTimeout(() => { + loadTimerRef.current = null; + setLoaded(true); + }, remaining); + return; + } + } + setLoaded(true); + }, []); + + const isLoading = showLoadingSpinner && spinnerVisible && imageUrl && !loaded && !error; const ImgOrFallback = initials && (!imageUrl || error) ? ( @@ -60,7 +105,7 @@ export const Avatar = (props: AvatarProps) => { transition: 'opacity 0.2s ease-in-out', }} onError={() => setError(true)} - onLoad={() => setLoaded(true)} + onLoad={handleImageLoad} size={imageFetchSize} /> ); From 437ada9446943fa0ebb2ef78faa3523216c43c92 Mon Sep 17 00:00:00 2001 From: Laura Beatris <48022589+LauraBeatris@users.noreply.github.com> Date: Mon, 12 Jan 2026 11:58:58 -0300 Subject: [PATCH 15/17] Bump max bundle size --- packages/clerk-js/bundlewatch.config.json | 4 ++-- .../src/core/resources/OrganizationCreationDefaults.ts | 6 ++++++ 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/packages/clerk-js/bundlewatch.config.json b/packages/clerk-js/bundlewatch.config.json index cbc3539426c..834dd41db10 100644 --- a/packages/clerk-js/bundlewatch.config.json +++ b/packages/clerk-js/bundlewatch.config.json @@ -1,8 +1,8 @@ { "files": [ { "path": "./dist/clerk.js", "maxSize": "538KB" }, - { "path": "./dist/clerk.browser.js", "maxSize": "63KB" }, - { "path": "./dist/clerk.chips.browser.js", "maxSize": "63KB" }, + { "path": "./dist/clerk.browser.js", "maxSize": "66KB" }, + { "path": "./dist/clerk.chips.browser.js", "maxSize": "66KB" }, { "path": "./dist/clerk.legacy.browser.js", "maxSize": "105KB" }, { "path": "./dist/clerk.no-rhc.js", "maxSize": "305KB" }, { "path": "./dist/clerk.headless*.js", "maxSize": "65KB" }, diff --git a/packages/clerk-js/src/core/resources/OrganizationCreationDefaults.ts b/packages/clerk-js/src/core/resources/OrganizationCreationDefaults.ts index cd4b8afb45a..94274a7a3ca 100644 --- a/packages/clerk-js/src/core/resources/OrganizationCreationDefaults.ts +++ b/packages/clerk-js/src/core/resources/OrganizationCreationDefaults.ts @@ -69,6 +69,12 @@ export class OrganizationCreationDefaults extends BaseResource implements Organi severity: this.advisory.severity, } : null, + form: { + name: this.form.name, + slug: this.form.slug, + logo: this.form.logo, + blur_hash: this.form.blurHash, + }, } as unknown as OrganizationCreationDefaultsJSONSnapshot; } } From 988a6daf062a4d953197f471bdf347e578b1d5e2 Mon Sep 17 00:00:00 2001 From: Laura Beatris <48022589+LauraBeatris@users.noreply.github.com> Date: Mon, 12 Jan 2026 15:02:41 -0300 Subject: [PATCH 16/17] Include organization name in advisory message --- packages/localizations/src/ar-SA.ts | 4 ++++ packages/localizations/src/be-BY.ts | 4 ++++ packages/localizations/src/bg-BG.ts | 4 ++++ packages/localizations/src/bn-IN.ts | 4 ++++ packages/localizations/src/ca-ES.ts | 4 ++++ packages/localizations/src/cs-CZ.ts | 4 ++++ packages/localizations/src/da-DK.ts | 4 ++++ packages/localizations/src/de-DE.ts | 4 ++++ packages/localizations/src/el-GR.ts | 4 ++++ packages/localizations/src/en-GB.ts | 4 ++++ packages/localizations/src/en-US.ts | 2 +- packages/localizations/src/es-CR.ts | 4 ++++ packages/localizations/src/es-ES.ts | 4 ++++ packages/localizations/src/es-MX.ts | 4 ++++ packages/localizations/src/es-UY.ts | 4 ++++ packages/localizations/src/fa-IR.ts | 4 ++++ packages/localizations/src/fi-FI.ts | 4 ++++ packages/localizations/src/fr-FR.ts | 4 ++++ packages/localizations/src/he-IL.ts | 4 ++++ packages/localizations/src/hi-IN.ts | 4 ++++ packages/localizations/src/hr-HR.ts | 4 ++++ packages/localizations/src/hu-HU.ts | 4 ++++ packages/localizations/src/id-ID.ts | 4 ++++ packages/localizations/src/is-IS.ts | 4 ++++ packages/localizations/src/it-IT.ts | 4 ++++ packages/localizations/src/ja-JP.ts | 4 ++++ packages/localizations/src/kk-KZ.ts | 4 ++++ packages/localizations/src/ko-KR.ts | 4 ++++ packages/localizations/src/mn-MN.ts | 4 ++++ packages/localizations/src/ms-MY.ts | 4 ++++ packages/localizations/src/nb-NO.ts | 4 ++++ packages/localizations/src/nl-BE.ts | 4 ++++ packages/localizations/src/nl-NL.ts | 4 ++++ packages/localizations/src/pl-PL.ts | 4 ++++ packages/localizations/src/pt-BR.ts | 4 ++++ packages/localizations/src/pt-PT.ts | 4 ++++ packages/localizations/src/ro-RO.ts | 4 ++++ packages/localizations/src/ru-RU.ts | 4 ++++ packages/localizations/src/sk-SK.ts | 4 ++++ packages/localizations/src/sr-RS.ts | 4 ++++ packages/localizations/src/sv-SE.ts | 4 ++++ packages/localizations/src/ta-IN.ts | 4 ++++ packages/localizations/src/te-IN.ts | 4 ++++ packages/localizations/src/th-TH.ts | 4 ++++ packages/localizations/src/tr-TR.ts | 4 ++++ packages/localizations/src/uk-UA.ts | 4 ++++ packages/localizations/src/vi-VN.ts | 4 ++++ packages/localizations/src/zh-CN.ts | 4 ++++ packages/localizations/src/zh-TW.ts | 4 ++++ packages/shared/src/types/localization.ts | 2 +- .../OrganizationCreationDefaultsAlert.tsx | 3 ++- 51 files changed, 196 insertions(+), 3 deletions(-) diff --git a/packages/localizations/src/ar-SA.ts b/packages/localizations/src/ar-SA.ts index 50d917bb24c..7b236645588 100644 --- a/packages/localizations/src/ar-SA.ts +++ b/packages/localizations/src/ar-SA.ts @@ -882,6 +882,10 @@ export const arSA: LocalizationResource = { actionLink: 'تسجيل الخروج', actionText: 'تم تسجيل الدخول كـ {{identifier}}', }, + alerts: { + organizationAlreadyExists: + 'توجد منظمة بالفعل لاسم الشركة المكتشف ({{organizationName}}) و {{organizationDomain}}. انضم عن طريق الدعوة.', + }, }, taskResetPassword: { formButtonPrimary: undefined, diff --git a/packages/localizations/src/be-BY.ts b/packages/localizations/src/be-BY.ts index 2fc534100a6..eeda34ac12d 100644 --- a/packages/localizations/src/be-BY.ts +++ b/packages/localizations/src/be-BY.ts @@ -890,6 +890,10 @@ export const beBY: LocalizationResource = { actionLink: 'Выйсці', actionText: 'Увайшлі як {{identifier}}', }, + alerts: { + organizationAlreadyExists: + 'Арганізацыя ўжо існуе для выяўленай назвы кампаніі ({{organizationName}}) і {{organizationDomain}}. Далучайцеся па запрашэнні.', + }, }, taskResetPassword: { formButtonPrimary: undefined, diff --git a/packages/localizations/src/bg-BG.ts b/packages/localizations/src/bg-BG.ts index bfb2dd00f07..7688d81eb0e 100644 --- a/packages/localizations/src/bg-BG.ts +++ b/packages/localizations/src/bg-BG.ts @@ -886,6 +886,10 @@ export const bgBG: LocalizationResource = { actionLink: 'Изход', actionText: 'Влязъл като {{identifier}}', }, + alerts: { + organizationAlreadyExists: + 'Организация вече съществува за откритото име на компанията ({{organizationName}}) и {{organizationDomain}}. Присъединете се чрез покана.', + }, }, taskResetPassword: { formButtonPrimary: undefined, diff --git a/packages/localizations/src/bn-IN.ts b/packages/localizations/src/bn-IN.ts index b1dcf6952f4..048968c279c 100644 --- a/packages/localizations/src/bn-IN.ts +++ b/packages/localizations/src/bn-IN.ts @@ -890,6 +890,10 @@ export const bnIN: LocalizationResource = { actionLink: 'সাইন আউট', actionText: '{{identifier}} হিসাবে সাইন ইন করা হয়েছে', }, + alerts: { + organizationAlreadyExists: + 'শনাক্ত করা কোম্পানির নাম ({{organizationName}}) এবং {{organizationDomain}}-এর জন্য একটি সংস্থা ইতিমধ্যেই বিদ্যমান। আমন্ত্রণের মাধ্যমে যোগ দিন।', + }, }, taskResetPassword: { formButtonPrimary: undefined, diff --git a/packages/localizations/src/ca-ES.ts b/packages/localizations/src/ca-ES.ts index 5d37a50d8d0..8b1c69e0ef4 100644 --- a/packages/localizations/src/ca-ES.ts +++ b/packages/localizations/src/ca-ES.ts @@ -885,6 +885,10 @@ export const caES: LocalizationResource = { actionLink: 'Tancar sessió', actionText: 'Sessió iniciada com a {{identifier}}', }, + alerts: { + organizationAlreadyExists: + "Ja existeix una organització per al nom d'empresa detectat ({{organizationName}}) i {{organizationDomain}}. Uneix-te per invitació.", + }, }, taskResetPassword: { formButtonPrimary: undefined, diff --git a/packages/localizations/src/cs-CZ.ts b/packages/localizations/src/cs-CZ.ts index 51bcab2c0a3..d077e4fcf69 100644 --- a/packages/localizations/src/cs-CZ.ts +++ b/packages/localizations/src/cs-CZ.ts @@ -896,6 +896,10 @@ export const csCZ: LocalizationResource = { actionLink: 'Odhlásit se', actionText: 'Přihlášen jako {{identifier}}', }, + alerts: { + organizationAlreadyExists: + 'Organizace již existuje pro detekovaný název společnosti ({{organizationName}}) a {{organizationDomain}}. Připojte se prostřednictvím pozvánky.', + }, }, taskResetPassword: { formButtonPrimary: undefined, diff --git a/packages/localizations/src/da-DK.ts b/packages/localizations/src/da-DK.ts index 912a16e1961..6f9372f75f6 100644 --- a/packages/localizations/src/da-DK.ts +++ b/packages/localizations/src/da-DK.ts @@ -883,6 +883,10 @@ export const daDK: LocalizationResource = { actionLink: 'Log ud', actionText: 'Logget ind som {{identifier}}', }, + alerts: { + organizationAlreadyExists: + 'Der findes allerede en organisation for det registrerede firmanavn ({{organizationName}}) og {{organizationDomain}}. Tilmeld dig via invitation.', + }, }, taskResetPassword: { formButtonPrimary: undefined, diff --git a/packages/localizations/src/de-DE.ts b/packages/localizations/src/de-DE.ts index c94e023a3ec..6052ff0ef37 100644 --- a/packages/localizations/src/de-DE.ts +++ b/packages/localizations/src/de-DE.ts @@ -901,6 +901,10 @@ export const deDE: LocalizationResource = { actionLink: 'Abmelden', actionText: 'Angemeldet als {{identifier}}', }, + alerts: { + organizationAlreadyExists: + 'Für den erkannten Firmennamen ({{organizationName}}) und {{organizationDomain}} existiert bereits eine Organisation. Treten Sie per Einladung bei.', + }, }, taskResetPassword: { formButtonPrimary: undefined, diff --git a/packages/localizations/src/el-GR.ts b/packages/localizations/src/el-GR.ts index 88d9194fed1..77002e95e97 100644 --- a/packages/localizations/src/el-GR.ts +++ b/packages/localizations/src/el-GR.ts @@ -888,6 +888,10 @@ export const elGR: LocalizationResource = { actionLink: 'Αποσύνδεση', actionText: 'Συνδεδεμένος ως {{identifier}}', }, + alerts: { + organizationAlreadyExists: + 'Υπάρχει ήδη οργανισμός για το ανιχνευμένο όνομα εταιρείας ({{organizationName}}) και {{organizationDomain}}. Εγγραφείτε μέσω πρόσκλησης.', + }, }, taskResetPassword: { formButtonPrimary: undefined, diff --git a/packages/localizations/src/en-GB.ts b/packages/localizations/src/en-GB.ts index a0223170508..e3cf2a2eb18 100644 --- a/packages/localizations/src/en-GB.ts +++ b/packages/localizations/src/en-GB.ts @@ -887,6 +887,10 @@ export const enGB: LocalizationResource = { actionLink: 'Sign out', actionText: 'Signed in as {{identifier}}', }, + alerts: { + organizationAlreadyExists: + 'An organisation already exists for the detected company name ({{organizationName}}) and {{organizationDomain}}. Join by invitation.', + }, }, taskResetPassword: { formButtonPrimary: undefined, diff --git a/packages/localizations/src/en-US.ts b/packages/localizations/src/en-US.ts index 6a9f5044bdc..15256e3e1c2 100644 --- a/packages/localizations/src/en-US.ts +++ b/packages/localizations/src/en-US.ts @@ -886,7 +886,7 @@ export const enUS: LocalizationResource = { }, alerts: { organizationAlreadyExists: - 'An organization already exists for the detected company name and {{email}}. Join by invitation.', + 'An organization already exists for the detected company name ({{organizationName}}) and {{organizationDomain}}. Join by invitation.', }, }, taskResetPassword: { diff --git a/packages/localizations/src/es-CR.ts b/packages/localizations/src/es-CR.ts index b704d982755..342cec4458a 100644 --- a/packages/localizations/src/es-CR.ts +++ b/packages/localizations/src/es-CR.ts @@ -893,6 +893,10 @@ export const esCR: LocalizationResource = { actionLink: 'Cerrar sesión', actionText: 'Conectado como {{identifier}}', }, + alerts: { + organizationAlreadyExists: + 'Ya existe una organización para el nombre de empresa detectado ({{organizationName}}) y {{organizationDomain}}. Únete por invitación.', + }, }, taskResetPassword: { formButtonPrimary: undefined, diff --git a/packages/localizations/src/es-ES.ts b/packages/localizations/src/es-ES.ts index 10b8b3b4a79..88449ca3e49 100644 --- a/packages/localizations/src/es-ES.ts +++ b/packages/localizations/src/es-ES.ts @@ -887,6 +887,10 @@ export const esES: LocalizationResource = { actionLink: 'Cerrar sesión', actionText: 'Sesión iniciada como {{identifier}}', }, + alerts: { + organizationAlreadyExists: + 'Ya existe una organización para el nombre de empresa detectado ({{organizationName}}) y {{organizationDomain}}. Únete por invitación.', + }, }, taskResetPassword: { formButtonPrimary: undefined, diff --git a/packages/localizations/src/es-MX.ts b/packages/localizations/src/es-MX.ts index 51aa523cc4e..b17ad42dee4 100644 --- a/packages/localizations/src/es-MX.ts +++ b/packages/localizations/src/es-MX.ts @@ -894,6 +894,10 @@ export const esMX: LocalizationResource = { actionLink: 'Cerrar sesión', actionText: 'Registrado como {{identifier}}', }, + alerts: { + organizationAlreadyExists: + 'Ya existe una organización para el nombre de empresa detectado ({{organizationName}}) y {{organizationDomain}}. Únete por invitación.', + }, }, taskResetPassword: { formButtonPrimary: undefined, diff --git a/packages/localizations/src/es-UY.ts b/packages/localizations/src/es-UY.ts index 94df1a845bb..fc90d0c1824 100644 --- a/packages/localizations/src/es-UY.ts +++ b/packages/localizations/src/es-UY.ts @@ -893,6 +893,10 @@ export const esUY: LocalizationResource = { actionLink: 'Cerrar sesión', actionText: 'Logueado como {{identifier}}', }, + alerts: { + organizationAlreadyExists: + 'Ya existe una organización para el nombre de empresa detectado ({{organizationName}}) y {{organizationDomain}}. Únete por invitación.', + }, }, taskResetPassword: { formButtonPrimary: undefined, diff --git a/packages/localizations/src/fa-IR.ts b/packages/localizations/src/fa-IR.ts index a38bf43bdd1..b33f584be12 100644 --- a/packages/localizations/src/fa-IR.ts +++ b/packages/localizations/src/fa-IR.ts @@ -897,6 +897,10 @@ export const faIR: LocalizationResource = { actionLink: 'خروج از همه حساب‌ها', actionText: 'می‌خواهید خارج شوید؟', }, + alerts: { + organizationAlreadyExists: + 'سازمانی برای نام شرکت شناسایی شده ({{organizationName}}) و {{organizationDomain}} از قبل وجود دارد. از طریق دعوتنامه بپیوندید.', + }, }, taskResetPassword: { formButtonPrimary: undefined, diff --git a/packages/localizations/src/fi-FI.ts b/packages/localizations/src/fi-FI.ts index 0c78950e678..5e75f899d20 100644 --- a/packages/localizations/src/fi-FI.ts +++ b/packages/localizations/src/fi-FI.ts @@ -886,6 +886,10 @@ export const fiFI: LocalizationResource = { actionLink: 'Kirjaudu ulos', actionText: 'Kirjautuneena käyttäjänä {{identifier}}', }, + alerts: { + organizationAlreadyExists: + 'Organisaatio on jo olemassa havaitulle yrityksen nimelle ({{organizationName}}) ja {{organizationDomain}}. Liity kutsulla.', + }, }, taskResetPassword: { formButtonPrimary: undefined, diff --git a/packages/localizations/src/fr-FR.ts b/packages/localizations/src/fr-FR.ts index ff468c1eb32..413f1ca25ee 100644 --- a/packages/localizations/src/fr-FR.ts +++ b/packages/localizations/src/fr-FR.ts @@ -902,6 +902,10 @@ export const frFR: LocalizationResource = { actionLink: 'Se déconnecter', actionText: 'Connecté en tant que {{identifier}}', }, + alerts: { + organizationAlreadyExists: + "Une organisation existe déjà pour le nom d'entreprise détecté ({{organizationName}}) et {{organizationDomain}}. Rejoignez par invitation.", + }, }, taskResetPassword: { formButtonPrimary: undefined, diff --git a/packages/localizations/src/he-IL.ts b/packages/localizations/src/he-IL.ts index 42564db8c6f..20c1cb0b225 100644 --- a/packages/localizations/src/he-IL.ts +++ b/packages/localizations/src/he-IL.ts @@ -876,6 +876,10 @@ export const heIL: LocalizationResource = { actionLink: 'התנתק', actionText: 'מחובר כ-{{identifier}}', }, + alerts: { + organizationAlreadyExists: + 'ארגון כבר קיים עבור שם החברה שזוהה ({{organizationName}}) ו-{{organizationDomain}}. הצטרף באמצעות הזמנה.', + }, }, taskResetPassword: { formButtonPrimary: undefined, diff --git a/packages/localizations/src/hi-IN.ts b/packages/localizations/src/hi-IN.ts index 6a9b336dd7c..3301b3847c2 100644 --- a/packages/localizations/src/hi-IN.ts +++ b/packages/localizations/src/hi-IN.ts @@ -891,6 +891,10 @@ export const hiIN: LocalizationResource = { actionLink: 'साइन आउट', actionText: '{{identifier}} के रूप में साइन इन किया गया', }, + alerts: { + organizationAlreadyExists: + 'पता लगाई गई कंपनी के नाम ({{organizationName}}) और {{organizationDomain}} के लिए एक संगठन पहले से मौजूद है। आमंत्रण द्वारा शामिल हों।', + }, }, taskResetPassword: { formButtonPrimary: undefined, diff --git a/packages/localizations/src/hr-HR.ts b/packages/localizations/src/hr-HR.ts index 529471130ae..67212f7440a 100644 --- a/packages/localizations/src/hr-HR.ts +++ b/packages/localizations/src/hr-HR.ts @@ -887,6 +887,10 @@ export const hrHR: LocalizationResource = { actionLink: 'Odjavi se', actionText: 'Prijavljen kao {{identifier}}', }, + alerts: { + organizationAlreadyExists: + 'Organizacija već postoji za otkriveni naziv tvrtke ({{organizationName}}) i {{organizationDomain}}. Pridružite se putem pozivnice.', + }, }, taskResetPassword: { formButtonPrimary: undefined, diff --git a/packages/localizations/src/hu-HU.ts b/packages/localizations/src/hu-HU.ts index 5ac10d193f1..aeb1c042c98 100644 --- a/packages/localizations/src/hu-HU.ts +++ b/packages/localizations/src/hu-HU.ts @@ -884,6 +884,10 @@ export const huHU: LocalizationResource = { actionLink: 'Kijelentkezés', actionText: 'Bejelentkezve: {{identifier}}', }, + alerts: { + organizationAlreadyExists: + 'Már létezik szervezet az észlelt cégnévhez ({{organizationName}}) és {{organizationDomain}}. Csatlakozz meghívással.', + }, }, taskResetPassword: { formButtonPrimary: undefined, diff --git a/packages/localizations/src/id-ID.ts b/packages/localizations/src/id-ID.ts index 7708f997cc3..0aaa672bfd5 100644 --- a/packages/localizations/src/id-ID.ts +++ b/packages/localizations/src/id-ID.ts @@ -892,6 +892,10 @@ export const idID: LocalizationResource = { actionLink: 'Keluar', actionText: 'Masuk sebagai {{identifier}}', }, + alerts: { + organizationAlreadyExists: + 'Organisasi sudah ada untuk nama perusahaan yang terdeteksi ({{organizationName}}) dan {{organizationDomain}}. Bergabung melalui undangan.', + }, }, taskResetPassword: { formButtonPrimary: undefined, diff --git a/packages/localizations/src/is-IS.ts b/packages/localizations/src/is-IS.ts index 0753fa95333..94c05eb6832 100644 --- a/packages/localizations/src/is-IS.ts +++ b/packages/localizations/src/is-IS.ts @@ -887,6 +887,10 @@ export const isIS: LocalizationResource = { actionLink: 'Skrá út', actionText: 'Skráður inn sem {{identifier}}', }, + alerts: { + organizationAlreadyExists: + 'Fyrirtæki er þegar til fyrir uppgötvaða fyrirtækjanafnið ({{organizationName}}) og {{organizationDomain}}. Skráðu þig með boði.', + }, }, taskResetPassword: { formButtonPrimary: undefined, diff --git a/packages/localizations/src/it-IT.ts b/packages/localizations/src/it-IT.ts index 59b78656282..c99073cde06 100644 --- a/packages/localizations/src/it-IT.ts +++ b/packages/localizations/src/it-IT.ts @@ -894,6 +894,10 @@ export const itIT: LocalizationResource = { actionLink: 'Esci', actionText: 'Accesso effettuato come {{identifier}}', }, + alerts: { + organizationAlreadyExists: + "Un'organizzazione esiste già per il nome dell'azienda rilevato ({{organizationName}}) e {{organizationDomain}}. Unisciti tramite invito.", + }, }, taskResetPassword: { formButtonPrimary: undefined, diff --git a/packages/localizations/src/ja-JP.ts b/packages/localizations/src/ja-JP.ts index ec125d4eba5..c58c5197cb4 100644 --- a/packages/localizations/src/ja-JP.ts +++ b/packages/localizations/src/ja-JP.ts @@ -897,6 +897,10 @@ export const jaJP: LocalizationResource = { actionLink: 'サインアウト', actionText: '{{identifier}} としてサインイン中', }, + alerts: { + organizationAlreadyExists: + '検出された会社名 ({{organizationName}}) と {{organizationDomain}} の組織がすでに存在します。招待を通じて参加してください。', + }, }, taskResetPassword: { formButtonPrimary: undefined, diff --git a/packages/localizations/src/kk-KZ.ts b/packages/localizations/src/kk-KZ.ts index 91b13a17427..828a94456a9 100644 --- a/packages/localizations/src/kk-KZ.ts +++ b/packages/localizations/src/kk-KZ.ts @@ -877,6 +877,10 @@ export const kkKZ: LocalizationResource = { actionLink: 'Шығу', actionText: '{{identifier}} ретінде кірді', }, + alerts: { + organizationAlreadyExists: + 'Анықталған компания атауы ({{organizationName}}) және {{organizationDomain}} үшін ұйым бұрыннан бар. Шақыру арқылы қосылыңыз.', + }, }, taskResetPassword: { formButtonPrimary: undefined, diff --git a/packages/localizations/src/ko-KR.ts b/packages/localizations/src/ko-KR.ts index 93110710f8e..a52bf6a1c22 100644 --- a/packages/localizations/src/ko-KR.ts +++ b/packages/localizations/src/ko-KR.ts @@ -878,6 +878,10 @@ export const koKR: LocalizationResource = { actionLink: '로그아웃', actionText: '{{identifier}}로 로그인됨', }, + alerts: { + organizationAlreadyExists: + '감지된 회사 이름 ({{organizationName}}) 및 {{organizationDomain}}에 대한 조직이 이미 존재합니다. 초대를 통해 가입하세요.', + }, }, taskResetPassword: { formButtonPrimary: undefined, diff --git a/packages/localizations/src/mn-MN.ts b/packages/localizations/src/mn-MN.ts index 652a8c324c5..57ea7f8fdd1 100644 --- a/packages/localizations/src/mn-MN.ts +++ b/packages/localizations/src/mn-MN.ts @@ -886,6 +886,10 @@ export const mnMN: LocalizationResource = { actionLink: 'Гарах', actionText: '{{identifier}} гэж нэвтэрсэн', }, + alerts: { + organizationAlreadyExists: + 'Илрүүлсэн компанийн нэр ({{organizationName}}) болон {{organizationDomain}}-д байгууллага аль хэдийн байна. Урилгаар нэгдэнэ үү.', + }, }, taskResetPassword: { formButtonPrimary: undefined, diff --git a/packages/localizations/src/ms-MY.ts b/packages/localizations/src/ms-MY.ts index 287fef9be36..7fc6ace2cbb 100644 --- a/packages/localizations/src/ms-MY.ts +++ b/packages/localizations/src/ms-MY.ts @@ -894,6 +894,10 @@ export const msMY: LocalizationResource = { actionLink: 'Daftar keluar', actionText: 'Log masuk sebagai {{identifier}}', }, + alerts: { + organizationAlreadyExists: + 'Organisasi sudah wujud untuk nama syarikat yang dikesan ({{organizationName}}) dan {{organizationDomain}}. Sertai melalui jemputan.', + }, }, taskResetPassword: { formButtonPrimary: undefined, diff --git a/packages/localizations/src/nb-NO.ts b/packages/localizations/src/nb-NO.ts index fa1e75dd586..a7ae414e371 100644 --- a/packages/localizations/src/nb-NO.ts +++ b/packages/localizations/src/nb-NO.ts @@ -884,6 +884,10 @@ export const nbNO: LocalizationResource = { actionLink: 'Logg ut', actionText: 'Innlogget som {{identifier}}', }, + alerts: { + organizationAlreadyExists: + 'En organisasjon eksisterer allerede for det oppdagede firmanavnet ({{organizationName}}) og {{organizationDomain}}. Bli med via invitasjon.', + }, }, taskResetPassword: { formButtonPrimary: undefined, diff --git a/packages/localizations/src/nl-BE.ts b/packages/localizations/src/nl-BE.ts index b3c4d8eb970..e614f1f3bcc 100644 --- a/packages/localizations/src/nl-BE.ts +++ b/packages/localizations/src/nl-BE.ts @@ -886,6 +886,10 @@ export const nlBE: LocalizationResource = { actionLink: 'Uitloggen', actionText: 'Ingelogd als {{identifier}}', }, + alerts: { + organizationAlreadyExists: + 'Er bestaat al een organisatie voor de gedetecteerde bedrijfsnaam ({{organizationName}}) en {{organizationDomain}}. Word lid via uitnodiging.', + }, }, taskResetPassword: { formButtonPrimary: undefined, diff --git a/packages/localizations/src/nl-NL.ts b/packages/localizations/src/nl-NL.ts index 978c9413aa1..a65897a463b 100644 --- a/packages/localizations/src/nl-NL.ts +++ b/packages/localizations/src/nl-NL.ts @@ -886,6 +886,10 @@ export const nlNL: LocalizationResource = { actionLink: 'Uitloggen', actionText: 'Ingelogd als {{identifier}}', }, + alerts: { + organizationAlreadyExists: + 'Er bestaat al een organisatie voor de gedetecteerde bedrijfsnaam ({{organizationName}}) en {{organizationDomain}}. Word lid via uitnodiging.', + }, }, taskResetPassword: { formButtonPrimary: undefined, diff --git a/packages/localizations/src/pl-PL.ts b/packages/localizations/src/pl-PL.ts index 30c915013b6..638d548ec93 100644 --- a/packages/localizations/src/pl-PL.ts +++ b/packages/localizations/src/pl-PL.ts @@ -890,6 +890,10 @@ export const plPL: LocalizationResource = { actionLink: 'Wyloguj', actionText: 'Zalogowano jako {{identifier}}', }, + alerts: { + organizationAlreadyExists: + 'Organizacja już istnieje dla wykrytej nazwy firmy ({{organizationName}}) i {{organizationDomain}}. Dołącz przez zaproszenie.', + }, }, taskResetPassword: { formButtonPrimary: 'Zresetuj hasło', diff --git a/packages/localizations/src/pt-BR.ts b/packages/localizations/src/pt-BR.ts index 07a8fedd0e6..2d5d3d87a63 100644 --- a/packages/localizations/src/pt-BR.ts +++ b/packages/localizations/src/pt-BR.ts @@ -898,6 +898,10 @@ export const ptBR: LocalizationResource = { actionLink: 'Sair', actionText: 'Conectado como {{identifier}}', }, + alerts: { + organizationAlreadyExists: + 'Uma organização já existe para o nome da empresa detectado ({{organizationName}}) e {{organizationDomain}}. Entre por convite.', + }, }, taskResetPassword: { formButtonPrimary: 'Resetar Senha', diff --git a/packages/localizations/src/pt-PT.ts b/packages/localizations/src/pt-PT.ts index c08709211ee..ef173c90304 100644 --- a/packages/localizations/src/pt-PT.ts +++ b/packages/localizations/src/pt-PT.ts @@ -884,6 +884,10 @@ export const ptPT: LocalizationResource = { actionLink: 'Terminar sessão', actionText: 'Sessão iniciada como {{identifier}}', }, + alerts: { + organizationAlreadyExists: + 'Já existe uma organização para o nome da empresa detetado ({{organizationName}}) e {{organizationDomain}}. Adira por convite.', + }, }, taskResetPassword: { formButtonPrimary: undefined, diff --git a/packages/localizations/src/ro-RO.ts b/packages/localizations/src/ro-RO.ts index d42538d29b9..53f7145d945 100644 --- a/packages/localizations/src/ro-RO.ts +++ b/packages/localizations/src/ro-RO.ts @@ -899,6 +899,10 @@ export const roRO: LocalizationResource = { actionLink: 'Deconectează-te', actionText: 'Autentificat ca {{identifier}}', }, + alerts: { + organizationAlreadyExists: + 'Există deja o organizație pentru numele companiei detectate ({{organizationName}}) și {{organizationDomain}}. Alătură-te prin invitație.', + }, }, taskResetPassword: { formButtonPrimary: undefined, diff --git a/packages/localizations/src/ru-RU.ts b/packages/localizations/src/ru-RU.ts index 5da404c6162..b57a12b9079 100644 --- a/packages/localizations/src/ru-RU.ts +++ b/packages/localizations/src/ru-RU.ts @@ -897,6 +897,10 @@ export const ruRU: LocalizationResource = { actionLink: 'Выйти', actionText: 'Вошли как {{identifier}}', }, + alerts: { + organizationAlreadyExists: + 'Организация уже существует для обнаруженного названия компании ({{organizationName}}) и {{organizationDomain}}. Присоединяйтесь по приглашению.', + }, }, taskResetPassword: { formButtonPrimary: undefined, diff --git a/packages/localizations/src/sk-SK.ts b/packages/localizations/src/sk-SK.ts index 4e4c362d0b6..e48dc7a0afc 100644 --- a/packages/localizations/src/sk-SK.ts +++ b/packages/localizations/src/sk-SK.ts @@ -890,6 +890,10 @@ export const skSK: LocalizationResource = { actionLink: 'Odhlásiť sa', actionText: 'Prihlásený ako {{identifier}}', }, + alerts: { + organizationAlreadyExists: + 'Organizácia už existuje pre zistený názov spoločnosti ({{organizationName}}) a {{organizationDomain}}. Pripojte sa prostredníctvom pozvánky.', + }, }, taskResetPassword: { formButtonPrimary: undefined, diff --git a/packages/localizations/src/sr-RS.ts b/packages/localizations/src/sr-RS.ts index a8f2efa6de2..f1719ff5b44 100644 --- a/packages/localizations/src/sr-RS.ts +++ b/packages/localizations/src/sr-RS.ts @@ -883,6 +883,10 @@ export const srRS: LocalizationResource = { actionLink: 'Odjavi se', actionText: 'Prijavljen kao {{identifier}}', }, + alerts: { + organizationAlreadyExists: + 'Organizacija već postoji za otkriveno ime kompanije ({{organizationName}}) i {{organizationDomain}}. Pridružite se putem pozivnice.', + }, }, taskResetPassword: { formButtonPrimary: undefined, diff --git a/packages/localizations/src/sv-SE.ts b/packages/localizations/src/sv-SE.ts index d2b076ce81d..9953b2b6dde 100644 --- a/packages/localizations/src/sv-SE.ts +++ b/packages/localizations/src/sv-SE.ts @@ -888,6 +888,10 @@ export const svSE: LocalizationResource = { actionLink: 'Logga ut', actionText: 'Inloggad som {{identifier}}', }, + alerts: { + organizationAlreadyExists: + 'En organisation finns redan för det upptäckta företagsnamnet ({{organizationName}}) och {{organizationDomain}}. Gå med via inbjudan.', + }, }, taskResetPassword: { formButtonPrimary: undefined, diff --git a/packages/localizations/src/ta-IN.ts b/packages/localizations/src/ta-IN.ts index cde79eff01d..e2152864bc9 100644 --- a/packages/localizations/src/ta-IN.ts +++ b/packages/localizations/src/ta-IN.ts @@ -893,6 +893,10 @@ export const taIN: LocalizationResource = { actionLink: 'வெளியேறு', actionText: '{{identifier}} என உள்நுழைந்துள்ளீர்கள்', }, + alerts: { + organizationAlreadyExists: + 'கண்டறியப்பட்ட நிறுவன பெயர் ({{organizationName}}) மற்றும் {{organizationDomain}} க்கு ஒரு அமைப்பு ஏற்கனவே உள்ளது. அழைப்பின் மூலம் சேரவும்.', + }, }, taskResetPassword: { formButtonPrimary: undefined, diff --git a/packages/localizations/src/te-IN.ts b/packages/localizations/src/te-IN.ts index f40b1f0a007..d603dab21b3 100644 --- a/packages/localizations/src/te-IN.ts +++ b/packages/localizations/src/te-IN.ts @@ -893,6 +893,10 @@ export const teIN: LocalizationResource = { actionLink: 'సైన్ అవుట్', actionText: '{{identifier}}గా సైన్ ఇన్ చేయబడింది', }, + alerts: { + organizationAlreadyExists: + 'గుర్తించిన కంపెనీ పేరు ({{organizationName}}) మరియు {{organizationDomain}} కోసం ఒక సంస్థ ఇప్పటికే ఉంది. ఆహ్వానం ద్వారా చేరండి.', + }, }, taskResetPassword: { formButtonPrimary: undefined, diff --git a/packages/localizations/src/th-TH.ts b/packages/localizations/src/th-TH.ts index 87398b079cd..303b29ca368 100644 --- a/packages/localizations/src/th-TH.ts +++ b/packages/localizations/src/th-TH.ts @@ -886,6 +886,10 @@ export const thTH: LocalizationResource = { actionLink: 'ออกจากระบบ', actionText: 'เข้าสู่ระบบในนาม {{identifier}}', }, + alerts: { + organizationAlreadyExists: + 'องค์กรสำหรับชื่อบริษัทที่ตรวจพบ ({{organizationName}}) และ {{organizationDomain}} มีอยู่แล้ว เข้าร่วมโดยการเชิญ', + }, }, taskResetPassword: { formButtonPrimary: undefined, diff --git a/packages/localizations/src/tr-TR.ts b/packages/localizations/src/tr-TR.ts index 8fef9ca99f3..ab80cec37a7 100644 --- a/packages/localizations/src/tr-TR.ts +++ b/packages/localizations/src/tr-TR.ts @@ -886,6 +886,10 @@ export const trTR: LocalizationResource = { actionLink: 'Çıkış yap', actionText: '{{identifier}} olarak giriş yapıldı', }, + alerts: { + organizationAlreadyExists: + 'Tespit edilen şirket adı ({{organizationName}}) ve {{organizationDomain}} için bir organizasyon zaten mevcut. Davetiye ile katılın.', + }, }, taskResetPassword: { formButtonPrimary: undefined, diff --git a/packages/localizations/src/uk-UA.ts b/packages/localizations/src/uk-UA.ts index 0bb8cd22a50..4f84903a806 100644 --- a/packages/localizations/src/uk-UA.ts +++ b/packages/localizations/src/uk-UA.ts @@ -882,6 +882,10 @@ export const ukUA: LocalizationResource = { actionLink: 'Вийти', actionText: 'Увійшли як {{identifier}}', }, + alerts: { + organizationAlreadyExists: + 'Організація вже існує для виявленої назви компанії ({{organizationName}}) та {{organizationDomain}}. Приєднуйтесь за запрошенням.', + }, }, taskResetPassword: { formButtonPrimary: undefined, diff --git a/packages/localizations/src/vi-VN.ts b/packages/localizations/src/vi-VN.ts index a50dacd425c..3120325bfcd 100644 --- a/packages/localizations/src/vi-VN.ts +++ b/packages/localizations/src/vi-VN.ts @@ -893,6 +893,10 @@ export const viVN: LocalizationResource = { actionLink: 'Đăng xuất', actionText: 'Đã đăng nhập với {{identifier}}', }, + alerts: { + organizationAlreadyExists: + 'Một tổ chức đã tồn tại cho tên công ty được phát hiện ({{organizationName}}) và {{organizationDomain}}. Tham gia bằng lời mời.', + }, }, taskResetPassword: { formButtonPrimary: undefined, diff --git a/packages/localizations/src/zh-CN.ts b/packages/localizations/src/zh-CN.ts index ffd83ee89e5..2eef007f398 100644 --- a/packages/localizations/src/zh-CN.ts +++ b/packages/localizations/src/zh-CN.ts @@ -872,6 +872,10 @@ export const zhCN: LocalizationResource = { actionLink: '退出', actionText: '已登录为 {{identifier}}', }, + alerts: { + organizationAlreadyExists: + '检测到的公司名称 ({{organizationName}}) 和 {{organizationDomain}} 已存在一个组织。请通过邀请加入。', + }, }, taskResetPassword: { formButtonPrimary: undefined, diff --git a/packages/localizations/src/zh-TW.ts b/packages/localizations/src/zh-TW.ts index 8c91fae57a7..72694a19896 100644 --- a/packages/localizations/src/zh-TW.ts +++ b/packages/localizations/src/zh-TW.ts @@ -873,6 +873,10 @@ export const zhTW: LocalizationResource = { actionLink: '登出', actionText: '已登入為 {{identifier}}', }, + alerts: { + organizationAlreadyExists: + '偵測到的公司名稱 ({{organizationName}}) 和 {{organizationDomain}} 已存在一個組織。請透過邀請加入。', + }, }, taskResetPassword: { formButtonPrimary: undefined, diff --git a/packages/shared/src/types/localization.ts b/packages/shared/src/types/localization.ts index 2cab1eab393..7c3b5ae0fc0 100644 --- a/packages/shared/src/types/localization.ts +++ b/packages/shared/src/types/localization.ts @@ -1315,7 +1315,7 @@ export type __internal_LocalizationResource = { subtitle: LocalizationValue; }; alerts: { - organizationAlreadyExists: LocalizationValue<'email'>; + organizationAlreadyExists: LocalizationValue<'organizationDomain' | 'organizationName'>; }; }; taskResetPassword: { diff --git a/packages/ui/src/components/SessionTasks/tasks/TaskChooseOrganization/OrganizationCreationDefaultsAlert.tsx b/packages/ui/src/components/SessionTasks/tasks/TaskChooseOrganization/OrganizationCreationDefaultsAlert.tsx index ed8192e5639..efef8ee9bb1 100644 --- a/packages/ui/src/components/SessionTasks/tasks/TaskChooseOrganization/OrganizationCreationDefaultsAlert.tsx +++ b/packages/ui/src/components/SessionTasks/tasks/TaskChooseOrganization/OrganizationCreationDefaultsAlert.tsx @@ -32,7 +32,8 @@ const advisoryToLocalizationKey = (advisory?: OrganizationCreationDefaultsResour switch (advisory.code) { case 'organization_already_exists': return localizationKeys('taskChooseOrganization.alerts.organizationAlreadyExists', { - email: advisory.meta.organization_domain, + organizationDomain: advisory.meta.organization_domain, + organizationName: advisory.meta.organization_name, }); default: return null; From ce1d55215526359120568c8a9959db8dec20ca59 Mon Sep 17 00:00:00 2001 From: Laura Beatris <48022589+LauraBeatris@users.noreply.github.com> Date: Mon, 12 Jan 2026 17:31:29 -0300 Subject: [PATCH 17/17] Fix unit tests --- .../__tests__/TaskChooseOrganization.test.tsx | 5 ++--- .../SessionTasks/tasks/TaskChooseOrganization/index.tsx | 6 +++++- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/packages/ui/src/components/SessionTasks/tasks/TaskChooseOrganization/__tests__/TaskChooseOrganization.test.tsx b/packages/ui/src/components/SessionTasks/tasks/TaskChooseOrganization/__tests__/TaskChooseOrganization.test.tsx index 624f39c8fe5..e3618dd7060 100644 --- a/packages/ui/src/components/SessionTasks/tasks/TaskChooseOrganization/__tests__/TaskChooseOrganization.test.tsx +++ b/packages/ui/src/components/SessionTasks/tasks/TaskChooseOrganization/__tests__/TaskChooseOrganization.test.tsx @@ -9,7 +9,6 @@ import { } from '@/ui/components/OrganizationSwitcher/__tests__/test-utils'; import { TaskChooseOrganization } from '..'; -import { findByText } from '@testing-library/react'; const { createFixtures } = bindCreateFixtures('TaskChooseOrganization'); @@ -373,7 +372,7 @@ describe('TaskChooseOrganization', () => { advisory: { code: 'organization_already_exists', severity: 'warning', - meta: { email: 'test@clerk.com' }, + meta: { organizationDomain: 'test@clerk.com', organizationName: 'Clerk' }, }, }), ); @@ -381,7 +380,7 @@ describe('TaskChooseOrganization', () => { const { findByText } = render(, { wrapper }); expect( - await findByText(/an organization already exists for the detected company name and test@clerk\.com/i), + await findByText(/an organization already exists for the detected company name (Clerk) and test@clerk\.com/i), ).toBeInTheDocument(); }); diff --git a/packages/ui/src/components/SessionTasks/tasks/TaskChooseOrganization/index.tsx b/packages/ui/src/components/SessionTasks/tasks/TaskChooseOrganization/index.tsx index 7913f736626..6862e1fc7c2 100644 --- a/packages/ui/src/components/SessionTasks/tasks/TaskChooseOrganization/index.tsx +++ b/packages/ui/src/components/SessionTasks/tasks/TaskChooseOrganization/index.tsx @@ -24,7 +24,11 @@ const TaskChooseOrganizationInternal = () => { 'organization-creation-defaults', ); - const isLoading = userMemberships?.isLoading || userInvitations?.isLoading || userSuggestions?.isLoading; + const isLoading = + userMemberships?.isLoading || + userInvitations?.isLoading || + userSuggestions?.isLoading || + organizationCreationDefaults?.isLoading; const hasExistingResources = !!(userMemberships?.count || userInvitations?.count || userSuggestions?.count); const isOrganizationCreationDisabled = !isLoading && !user?.createOrganizationEnabled && !hasExistingResources;