diff --git a/.changeset/curvy-hounds-fix.md b/.changeset/curvy-hounds-fix.md new file mode 100644 index 00000000000..0bf819cb503 --- /dev/null +++ b/.changeset/curvy-hounds-fix.md @@ -0,0 +1,8 @@ +--- +'@clerk/localizations': minor +'@clerk/clerk-js': minor +'@clerk/shared': minor +'@clerk/ui': minor +--- + +Introduce organization domains with TXT verification on self-serve SSO flow diff --git a/packages/clerk-js/src/core/resources/Organization.ts b/packages/clerk-js/src/core/resources/Organization.ts index c3c91cb4b73..515d19a191d 100644 --- a/packages/clerk-js/src/core/resources/Organization.ts +++ b/packages/clerk-js/src/core/resources/Organization.ts @@ -2,6 +2,7 @@ import type { AddMemberParams, ClerkPaginatedResponse, ClerkResourceReloadParams, + CreateOrganizationDomainParams, CreateOrganizationEnterpriseConnectionParams, CreateOrganizationParams, DeletedObjectJSON, @@ -24,6 +25,8 @@ import type { InviteMembersParams, OrganizationDomainJSON, OrganizationDomainResource, + OrganizationDomainsBulkOwnershipVerificationJSON, + OrganizationDomainsBulkOwnershipVerificationResource, OrganizationInvitationJSON, OrganizationInvitationResource, OrganizationJSON, @@ -134,11 +137,17 @@ export class Organization extends BaseResource implements OrganizationResource { getDomains = async ( getDomainParams?: GetDomainsParams, ): Promise> => { + const { enrollmentMode, ...rest } = getDomainParams || {}; + const search = convertPageToOffsetSearchParams(rest); + if (enrollmentMode) { + search.set('enrollment_mode', enrollmentMode); + } + return await BaseResource._fetch( { path: `/organizations/${this.id}/domains`, method: 'GET', - search: convertPageToOffsetSearchParams(getDomainParams), + search, }, { forceUpdateClient: true, @@ -282,8 +291,46 @@ export class Organization extends BaseResource implements OrganizationResource { }); }; - createDomain = async (name: string): Promise => { - return OrganizationDomain.create(this.id, { name }); + createDomain = async ( + name: string, + params?: Pick, + ): Promise => { + return OrganizationDomain.create(this.id, { name, enrollmentMode: params?.enrollmentMode }); + }; + + prepareOwnershipVerification = async ( + domainIds: string[], + ): Promise => { + return this.bulkOwnershipVerification('prepare_ownership_verification', domainIds); + }; + + attemptOwnershipVerification = async ( + domainIds: string[], + ): Promise => { + return this.bulkOwnershipVerification('attempt_ownership_verification', domainIds); + }; + + private bulkOwnershipVerification = async ( + action: 'prepare_ownership_verification' | 'attempt_ownership_verification', + domainIds: string[], + ): Promise => { + const { data, errors } = ( + await BaseResource._fetch( + { + path: `/organizations/${this.id}/domains/${action}/bulk`, + method: 'POST', + body: { organization_domain_ids: domainIds } as any, + }, + { + forceUpdateClient: true, + }, + ) + )?.response as unknown as OrganizationDomainsBulkOwnershipVerificationJSON; + + return { + data: (data ?? []).map(domain => new OrganizationDomain(domain)), + errors: errors ?? [], + }; }; getMemberships: GetMemberships = async getMembershipsParams => { diff --git a/packages/clerk-js/src/core/resources/OrganizationDomain.ts b/packages/clerk-js/src/core/resources/OrganizationDomain.ts index 9200f31388e..12256963e51 100644 --- a/packages/clerk-js/src/core/resources/OrganizationDomain.ts +++ b/packages/clerk-js/src/core/resources/OrganizationDomain.ts @@ -1,6 +1,8 @@ import type { AttemptAffiliationVerificationParams, + CreateOrganizationDomainParams, OrganizationDomainJSON, + OrganizationDomainOwnershipVerification, OrganizationDomainResource, OrganizationDomainVerification, OrganizationEnrollmentMode, @@ -17,6 +19,8 @@ export class OrganizationDomain extends BaseResource implements OrganizationDoma organizationId!: string; enrollmentMode!: OrganizationEnrollmentMode; verification!: OrganizationDomainVerification | null; + affiliationVerification!: OrganizationDomainVerification | null; + ownershipVerification!: OrganizationDomainOwnershipVerification | null; affiliationEmailAddress!: string | null; createdAt!: Date; updatedAt!: Date; @@ -28,12 +32,15 @@ export class OrganizationDomain extends BaseResource implements OrganizationDoma this.fromJSON(data); } - static async create(organizationId: string, { name }: { name: string }): Promise { + static async create( + organizationId: string, + { name, enrollmentMode }: CreateOrganizationDomainParams, + ): Promise { const json = ( await BaseResource._fetch({ path: `/organizations/${organizationId}/domains`, method: 'POST', - body: { name } as any, + body: { name, enrollment_mode: enrollmentMode } as any, }) )?.response as unknown as OrganizationDomainJSON; return new OrganizationDomain(json); @@ -81,16 +88,40 @@ export class OrganizationDomain extends BaseResource implements OrganizationDoma this.affiliationEmailAddress = data.affiliation_email_address; this.totalPendingSuggestions = data.total_pending_suggestions; this.totalPendingInvitations = data.total_pending_invitations; - if (data.verification) { - this.verification = { - status: data.verification.status, - strategy: data.verification.strategy, - attempts: data.verification.attempts, - expiresAt: unixEpochToDate(data.verification.expires_at), + + const affiliationVerificationJSON = data.affiliation_verification ?? data.verification; + if (affiliationVerificationJSON) { + const affiliationVerification: OrganizationDomainVerification = { + status: affiliationVerificationJSON.status, + strategy: affiliationVerificationJSON.strategy, + attempts: affiliationVerificationJSON.attempts, + expiresAt: unixEpochToDate(affiliationVerificationJSON.expires_at), }; + this.affiliationVerification = affiliationVerification; + // Deprecated alias, kept in sync for backwards compatibility. + this.verification = affiliationVerification; } else { + this.affiliationVerification = null; this.verification = null; } + + if (data.ownership_verification) { + this.ownershipVerification = { + status: data.ownership_verification.status, + strategy: data.ownership_verification.strategy, + attempts: data.ownership_verification.attempts, + expiresAt: data.ownership_verification.expire_at + ? unixEpochToDate(data.ownership_verification.expire_at) + : null, + verifiedAt: data.ownership_verification.verified_at + ? unixEpochToDate(data.ownership_verification.verified_at) + : null, + txtRecordName: data.ownership_verification.txt_record_name ?? null, + txtRecordValue: data.ownership_verification.txt_record_value ?? null, + }; + } else { + this.ownershipVerification = null; + } } return this; } diff --git a/packages/localizations/src/ar-SA.ts b/packages/localizations/src/ar-SA.ts index bc77a14870a..c2c51b8f0ec 100644 --- a/packages/localizations/src/ar-SA.ts +++ b/packages/localizations/src/ar-SA.ts @@ -196,29 +196,27 @@ export const arSA: LocalizationResource = { }, warning: 'بمجرد اختيار المزود لا يمكنك التغيير مرة أخرى حتى انتهاء التكوين', }, - verifyEmailDomainStep: { - title: 'التحقق من البريد الإلكتروني', - subtitle: 'تحقق من عنوان البريد الإلكتروني الذي تريد تفعيل اتصال المؤسسة عليه.', - addEmailAddress: { - formTitle: 'نحتاج إلى بريدك الإلكتروني', - formSubtitle: 'للبدء، نحتاج إلى عنوان بريدك الإلكتروني', - inputPlaceholder: 'name@company.com', - inputLabel: 'عنوان البريد الإلكتروني', - }, - emailCode: { - formTitle: 'تحقق من عنوان بريدك الإلكتروني', - formSubtitle: 'أدخل رمز التحقق المرسل إلى {{identifier}}', - resendButton: 'لم تتلقَّ الرمز؟ إعادة الإرسال', - verified: { - title: 'لقد تلقينا بريدك الإلكتروني', - subtitle: 'لقد تحققت من عنوان بريدك الإلكتروني التالي', - inputLabel: 'عنوان البريد الإلكتروني الذي تم التحقق منه', + organizationDomainsStep: { + title: 'إضافة نطاقات SSO', + subtitle: 'أضِف نطاقات مؤسستك المستخدمة لتسجيل الدخول وتحقّق من ملكيتها.', + formFieldLabel__domain: 'النطاقات', + formFieldInputPlaceholder__domain: 'اكتب نطاقك هنا وانقر على إضافة للبدء', + formButtonPrimary__add: 'إضافة', + domainSuggestion: { + messageLabel: 'بريدك الإلكتروني يستخدم {{domain}}. هل تريد إضافته؟', + formButtonPrimary__add: 'إضافة {{domain}}', + }, + domainCard: { + badge__verified: 'تم التحقق', + badge__unverified: 'لم يتم التحقق', + verifiedAtLabel: "تم التحقق في {{ date | shortDate('en-US') }}", + txtRecord: { + instructions: 'أضِف سجل TXT هذا إلى مزوّد DNS الخاص بك. سنتحقق تلقائيًا بمجرد أن يصبح السجل نشطًا.', + typeLabel: 'النوع', + hostLabel: 'المضيف / الاسم', + valueLabel: 'القيمة', }, }, - domainTaken: { - title: 'هذا النطاق ({{domain}}) لديه بالفعل اتصال SSO', - subtitle: 'تواصل مع مسؤول التطبيق للحصول على الوصول من خلال الاتصال الحالي.', - }, }, }, createOrganization: { diff --git a/packages/localizations/src/be-BY.ts b/packages/localizations/src/be-BY.ts index b24fe2a2a0e..15bcc3e190b 100644 --- a/packages/localizations/src/be-BY.ts +++ b/packages/localizations/src/be-BY.ts @@ -196,29 +196,28 @@ export const beBY: LocalizationResource = { }, warning: 'Пасля выбару правайдэра вы не зможаце змяніць яго, пакуль не скончыце канфігурацыю', }, - verifyEmailDomainStep: { - title: 'Пацвердзіць адрас электроннай пошты', - subtitle: 'Пацвердзіце адрас электроннай пошты, на якім вы хочаце ўключыць карпаратыўнае падключэнне.', - addEmailAddress: { - formTitle: 'Нам патрэбна ваша пошта', - formSubtitle: 'Каб пачаць, нам спатрэбіцца ваш адрас электроннай пошты', - inputPlaceholder: 'name@company.com', - inputLabel: 'Адрас электроннай пошты', - }, - emailCode: { - formTitle: 'Пацвердзіце ваш адрас электроннай пошты', - formSubtitle: 'Увядзіце код пацверджання, дасланы на {{identifier}}', - resendButton: 'Не атрымалі код? Адправіць паўторна', - verified: { - title: 'Мы атрымалі вашу пошту', - subtitle: 'Вы пацвердзілі свой адрас электроннай пошты з наступнай поштай', - inputLabel: 'Пацверджаны адрас электроннай пошты', + organizationDomainsStep: { + title: 'Дадаць дамены SSO', + subtitle: 'Дадайце і пацвердзіце права ўласнасці на дамены, якія ваша арганізацыя выкарыстоўвае для ўваходу.', + formFieldLabel__domain: 'Дамены', + formFieldInputPlaceholder__domain: 'Увядзіце свой дамен тут і націсніце «Дадаць», каб пачаць', + formButtonPrimary__add: 'Дадаць', + domainSuggestion: { + messageLabel: 'Ваша электронная пошта выкарыстоўвае {{domain}}. Хочаце дадаць яго?', + formButtonPrimary__add: 'Дадаць {{domain}}', + }, + domainCard: { + badge__verified: 'Пацверджана', + badge__unverified: 'Не пацверджана', + verifiedAtLabel: "Пацверджана {{ date | shortDate('be-BY') }}", + txtRecord: { + instructions: + 'Дадайце гэты TXT-запіс да вашага DNS-правайдара. Мы аўтаматычна выканаем праверку, як толькі запіс стане актыўным.', + typeLabel: 'Тып', + hostLabel: 'Хост / Імя', + valueLabel: 'Значэнне', }, }, - domainTaken: { - title: 'Гэты дамен ({{domain}}) ужо мае SSO-падключэнне', - subtitle: 'Звяжыцеся з адміністратарам прыкладання, каб атрымаць доступ праз існуючае падключэнне.', - }, }, }, createOrganization: { diff --git a/packages/localizations/src/bg-BG.ts b/packages/localizations/src/bg-BG.ts index 1f4fd93ba1e..3fafa4fca48 100644 --- a/packages/localizations/src/bg-BG.ts +++ b/packages/localizations/src/bg-BG.ts @@ -197,29 +197,28 @@ export const bgBG: LocalizationResource = { }, warning: 'След като изберете доставчик, не можете да го промените, докато конфигурацията не приключи', }, - verifyEmailDomainStep: { - title: 'Потвърди имейл адреса', - subtitle: 'Потвърдете имейл адреса, на който искате да активирате корпоративната връзка.', - addEmailAddress: { - formTitle: 'Нуждаем се от вашия имейл', - formSubtitle: 'За да започнем, ще ни е необходим вашият имейл адрес', - inputPlaceholder: 'name@company.com', - inputLabel: 'Имейл адрес', - }, - emailCode: { - formTitle: 'Потвърдете имейл адреса си', - formSubtitle: 'Въведете кода за потвърждение, изпратен на {{identifier}}', - resendButton: 'Не получихте код? Изпрати отново', - verified: { - title: 'Получихме имейла ви', - subtitle: 'Потвърдихте имейл адреса си със следния имейл', - inputLabel: 'Потвърден имейл адрес', + organizationDomainsStep: { + title: 'Добавяне на SSO домейни', + subtitle: 'Добавете и потвърдете собствеността върху домейните, които вашата организация използва за вход.', + formFieldLabel__domain: 'Домейни', + formFieldInputPlaceholder__domain: 'Въведете домейна си тук и щракнете върху добавяне, за да започнете', + formButtonPrimary__add: 'Добави', + domainSuggestion: { + messageLabel: 'Вашият имейл използва {{domain}}. Искате ли да го добавите?', + formButtonPrimary__add: 'Добавяне на {{domain}}', + }, + domainCard: { + badge__verified: 'Потвърден', + badge__unverified: 'Непотвърден', + verifiedAtLabel: "Потвърден на {{ date | shortDate('bg-BG') }}", + txtRecord: { + instructions: + 'Добавете този TXT запис към вашия DNS доставчик. Ще потвърдим автоматично, след като записът стане активен.', + typeLabel: 'Тип', + hostLabel: 'Хост / Име', + valueLabel: 'Стойност', }, }, - domainTaken: { - title: 'Този домейн ({{domain}}) вече има SSO връзка', - subtitle: 'Свържете се с администратора на приложението, за да получите достъп чрез съществуващата връзка.', - }, }, }, createOrganization: { diff --git a/packages/localizations/src/bn-IN.ts b/packages/localizations/src/bn-IN.ts index 4b962b33ef1..2356d8beb2d 100644 --- a/packages/localizations/src/bn-IN.ts +++ b/packages/localizations/src/bn-IN.ts @@ -202,29 +202,28 @@ export const bnIN: LocalizationResource = { }, warning: 'একবার প্রদানকারী নির্বাচন করার পরে, কনফিগারেশন শেষ না হওয়া পর্যন্ত আপনি আবার পরিবর্তন করতে পারবেন না', }, - verifyEmailDomainStep: { - title: 'ইমেইল ঠিকানা যাচাই করুন', - subtitle: 'যে ইমেইল ঠিকানায় আপনি এন্টারপ্রাইজ সংযোগ সক্রিয় করতে চান তা যাচাই করুন।', - addEmailAddress: { - formTitle: 'আমাদের আপনার ইমেইল প্রয়োজন', - formSubtitle: 'শুরু করতে আমাদের আপনার ইমেইল ঠিকানা প্রয়োজন হবে', - inputPlaceholder: 'name@company.com', - inputLabel: 'ইমেইল ঠিকানা', - }, - emailCode: { - formTitle: 'আপনার ইমেইল ঠিকানা যাচাই করুন', - formSubtitle: '{{identifier}} এ পাঠানো যাচাইকরণ কোড লিখুন', - resendButton: 'কোড পাননি? পুনরায় পাঠান', - verified: { - title: 'আমরা আপনার ইমেইল পেয়েছি', - subtitle: 'আপনি নিম্নলিখিত ইমেইল দিয়ে আপনার ইমেইল ঠিকানা যাচাই করেছেন', - inputLabel: 'যাচাইকৃত ইমেইল ঠিকানা', + organizationDomainsStep: { + title: 'SSO ডোমেইন যোগ করুন', + subtitle: 'আপনার প্রতিষ্ঠান সাইন ইন করতে যে ডোমেইনগুলি ব্যবহার করে তার মালিকানা যোগ করুন এবং যাচাই করুন।', + formFieldLabel__domain: 'ডোমেইন', + formFieldInputPlaceholder__domain: 'আপনার ডোমেইন এখানে টাইপ করুন এবং শুরু করতে যোগ করুন-এ ক্লিক করুন', + formButtonPrimary__add: 'যোগ করুন', + domainSuggestion: { + messageLabel: 'আপনার ইমেল {{domain}} ব্যবহার করে। আপনি কি এটি যোগ করতে চান?', + formButtonPrimary__add: '{{domain}} যোগ করুন', + }, + domainCard: { + badge__verified: 'যাচাই করা হয়েছে', + badge__unverified: 'যাচাই করা হয়নি', + verifiedAtLabel: "{{ date | shortDate('bn-IN') }} তারিখে যাচাই করা হয়েছে", + txtRecord: { + instructions: + 'আপনার DNS প্রদানকারীতে এই TXT রেকর্ডটি যোগ করুন। রেকর্ডটি সক্রিয় হলে আমরা স্বয়ংক্রিয়ভাবে যাচাই করব।', + typeLabel: 'ধরন', + hostLabel: 'হোস্ট / নাম', + valueLabel: 'মান', }, }, - domainTaken: { - title: 'এই ডোমেইনে ({{domain}}) ইতিমধ্যে একটি SSO সংযোগ রয়েছে', - subtitle: 'বিদ্যমান সংযোগের মাধ্যমে অ্যাক্সেস পেতে অ্যাপ্লিকেশন প্রশাসকের সাথে যোগাযোগ করুন।', - }, }, }, createOrganization: { diff --git a/packages/localizations/src/ca-ES.ts b/packages/localizations/src/ca-ES.ts index 3b5339884e9..37b471e5250 100644 --- a/packages/localizations/src/ca-ES.ts +++ b/packages/localizations/src/ca-ES.ts @@ -203,29 +203,28 @@ export const caES: LocalizationResource = { }, warning: 'Un cop seleccionat un proveïdor no podreu canviar-lo fins que la configuració hagi finalitzat', }, - verifyEmailDomainStep: { - title: 'Verifica el correu electrònic', - subtitle: "Verifica l'adreça de correu electrònic on vols habilitar la connexió empresarial.", - addEmailAddress: { - formTitle: 'Necessitem el teu correu', - formSubtitle: 'Per començar necessitem la teva adreça de correu electrònic', - inputPlaceholder: 'name@company.com', - inputLabel: 'Adreça de correu electrònic', - }, - emailCode: { - formTitle: "Verifica l'adreça de correu electrònic", - formSubtitle: 'Introdueix el codi de verificació enviat a {{identifier}}', - resendButton: 'No has rebut el codi? Reenvia', - verified: { - title: 'Hem rebut el teu correu', - subtitle: 'Has verificat la teva adreça de correu electrònic amb el següent correu', - inputLabel: 'Adreça de correu electrònic verificada', + organizationDomainsStep: { + title: 'Afegeix dominis SSO', + subtitle: 'Afegeix i verifica la propietat dels dominis que la teva organització utilitza per iniciar sessió.', + formFieldLabel__domain: 'Dominis', + formFieldInputPlaceholder__domain: 'Escriu aquí el teu domini i fes clic a Afegeix per començar', + formButtonPrimary__add: 'Afegeix', + domainSuggestion: { + messageLabel: 'El teu correu electrònic utilitza {{domain}}. Vols afegir-lo?', + formButtonPrimary__add: 'Afegeix {{domain}}', + }, + domainCard: { + badge__verified: 'Verificat', + badge__unverified: 'Sense verificar', + verifiedAtLabel: "Verificat el {{ date | shortDate('ca-ES') }}", + txtRecord: { + instructions: + 'Afegeix aquest registre TXT al teu proveïdor de DNS. El verificarem automàticament un cop el registre estigui actiu.', + typeLabel: 'Tipus', + hostLabel: 'Amfitrió / Nom', + valueLabel: 'Valor', }, }, - domainTaken: { - title: 'Aquest domini ({{domain}}) ja té una connexió SSO', - subtitle: "Contacta amb l'administrador de l'aplicació per obtenir accés a través de la connexió existent.", - }, }, }, createOrganization: { diff --git a/packages/localizations/src/cs-CZ.ts b/packages/localizations/src/cs-CZ.ts index 3a5c09c266f..29448d1f0df 100644 --- a/packages/localizations/src/cs-CZ.ts +++ b/packages/localizations/src/cs-CZ.ts @@ -200,29 +200,28 @@ export const csCZ: LocalizationResource = { }, warning: 'Jakmile vyberete poskytovatele, nelze ho změnit, dokud nebude konfigurace dokončena', }, - verifyEmailDomainStep: { - title: 'Ověřit e-mailovou adresu', - subtitle: 'Ověřte e-mailovou adresu, na které chcete povolit podnikové připojení.', - addEmailAddress: { - formTitle: 'Potřebujeme váš e-mail', - formSubtitle: 'K zahájení budeme potřebovat vaši e-mailovou adresu', - inputPlaceholder: 'name@company.com', - inputLabel: 'E-mailová adresa', - }, - emailCode: { - formTitle: 'Ověřte svou e-mailovou adresu', - formSubtitle: 'Zadejte ověřovací kód odeslaný na {{identifier}}', - resendButton: 'Neobdrželi jste kód? Odeslat znovu', - verified: { - title: 'Máme váš e-mail', - subtitle: 'Ověřili jste svou e-mailovou adresu pomocí následujícího e-mailu', - inputLabel: 'Ověřená e-mailová adresa', + organizationDomainsStep: { + title: 'Přidat domény SSO', + subtitle: 'Přidejte a ověřte vlastnictví domén, které vaše organizace používá k přihlášení.', + formFieldLabel__domain: 'Domény', + formFieldInputPlaceholder__domain: 'Zde zadejte svou doménu a kliknutím na přidat začněte', + formButtonPrimary__add: 'Přidat', + domainSuggestion: { + messageLabel: 'Váš e-mail používá {{domain}}. Chcete jej přidat?', + formButtonPrimary__add: 'Přidat {{domain}}', + }, + domainCard: { + badge__verified: 'Ověřeno', + badge__unverified: 'Neověřeno', + verifiedAtLabel: "Ověřeno {{ date | shortDate('cs-CZ') }}", + txtRecord: { + instructions: + 'Přidejte tento záznam TXT u svého poskytovatele DNS. Jakmile bude záznam aktivní, automaticky jej ověříme.', + typeLabel: 'Typ', + hostLabel: 'Hostitel / Název', + valueLabel: 'Hodnota', }, }, - domainTaken: { - title: 'Tato doména ({{domain}}) již má SSO připojení', - subtitle: 'Kontaktujte administrátora aplikace, abyste získali přístup prostřednictvím stávajícího připojení.', - }, }, }, createOrganization: { diff --git a/packages/localizations/src/da-DK.ts b/packages/localizations/src/da-DK.ts index 53f74d0a66f..8f3e4d292a5 100644 --- a/packages/localizations/src/da-DK.ts +++ b/packages/localizations/src/da-DK.ts @@ -196,29 +196,27 @@ export const daDK: LocalizationResource = { }, warning: 'Når en udbyder er valgt, kan du ikke ændre den, før konfigurationen er færdig', }, - verifyEmailDomainStep: { - title: 'Bekræft e-mailadresse', - subtitle: 'Bekræft den e-mailadresse, du vil aktivere virksomhedsforbindelsen på.', - addEmailAddress: { - formTitle: 'Vi har brug for din e-mail', - formSubtitle: 'For at starte har vi brug for din e-mailadresse', - inputPlaceholder: 'name@company.com', - inputLabel: 'E-mailadresse', - }, - emailCode: { - formTitle: 'Bekræft din e-mailadresse', - formSubtitle: 'Indtast bekræftelseskoden sendt til {{identifier}}', - resendButton: 'Modtog du ingen kode? Send igen', - verified: { - title: 'Vi har modtaget din e-mail', - subtitle: 'Du har bekræftet din e-mailadresse med følgende e-mail', - inputLabel: 'Bekræftet e-mailadresse', + organizationDomainsStep: { + title: 'Tilføj SSO-domæner', + subtitle: 'Tilføj og bekræft ejerskabet af de domæner, din organisation bruger til at logge ind.', + formFieldLabel__domain: 'Domæner', + formFieldInputPlaceholder__domain: 'Skriv dit domæne her, og klik på tilføj for at starte', + formButtonPrimary__add: 'Tilføj', + domainSuggestion: { + messageLabel: 'Din e-mail bruger {{domain}}. Vil du tilføje det?', + formButtonPrimary__add: 'Tilføj {{domain}}', + }, + domainCard: { + badge__verified: 'Bekræftet', + badge__unverified: 'Ikke bekræftet', + verifiedAtLabel: "Bekræftet den {{ date | shortDate('da-DK') }}", + txtRecord: { + instructions: 'Tilføj denne TXT-post hos din DNS-udbyder. Vi bekræfter automatisk, så snart posten er aktiv.', + typeLabel: 'Type', + hostLabel: 'Vært / Navn', + valueLabel: 'Værdi', }, }, - domainTaken: { - title: 'Dette domæne ({{domain}}) har allerede en SSO-forbindelse', - subtitle: 'Kontakt applikationens administrator for at få adgang via den eksisterende forbindelse.', - }, }, }, createOrganization: { diff --git a/packages/localizations/src/de-DE.ts b/packages/localizations/src/de-DE.ts index 5ebc5a9025c..dd504371d50 100644 --- a/packages/localizations/src/de-DE.ts +++ b/packages/localizations/src/de-DE.ts @@ -203,30 +203,29 @@ export const deDE: LocalizationResource = { warning: 'Sobald ein Anbieter ausgewählt ist, können Sie ihn nicht mehr ändern, bis die Konfiguration abgeschlossen ist', }, - verifyEmailDomainStep: { - title: 'E-Mail-Adresse verifizieren', - subtitle: 'Verifizieren Sie die E-Mail-Adresse, für die Sie die Unternehmensverbindung aktivieren möchten.', - addEmailAddress: { - formTitle: 'Wir benötigen Ihre E-Mail', - formSubtitle: 'Um zu beginnen, benötigen wir Ihre E-Mail-Adresse', - inputPlaceholder: 'name@company.com', - inputLabel: 'E-Mail-Adresse', - }, - emailCode: { - formTitle: 'Verifizieren Sie Ihre E-Mail-Adresse', - formSubtitle: 'Geben Sie den an {{identifier}} gesendeten Verifizierungscode ein', - resendButton: 'Keinen Code erhalten? Erneut senden', - verified: { - title: 'Wir haben Ihre E-Mail erhalten', - subtitle: 'Sie haben Ihre E-Mail-Adresse mit der folgenden E-Mail verifiziert', - inputLabel: 'Verifizierte E-Mail-Adresse', + organizationDomainsStep: { + title: 'SSO-Domains hinzufügen', + subtitle: + 'Fügen Sie die Domains hinzu, die Ihre Organisation zur Anmeldung verwendet, und verifizieren Sie deren Eigentümerschaft.', + formFieldLabel__domain: 'Domains', + formFieldInputPlaceholder__domain: 'Geben Sie hier Ihre Domain ein und klicken Sie zum Starten auf „Hinzufügen"', + formButtonPrimary__add: 'Hinzufügen', + domainSuggestion: { + messageLabel: 'Ihre E-Mail verwendet {{domain}}. Möchten Sie sie hinzufügen?', + formButtonPrimary__add: '{{domain}} hinzufügen', + }, + domainCard: { + badge__verified: 'Verifiziert', + badge__unverified: 'Nicht verifiziert', + verifiedAtLabel: "Verifiziert am {{ date | shortDate('de-DE') }}", + txtRecord: { + instructions: + 'Fügen Sie diesen TXT-Eintrag bei Ihrem DNS-Anbieter hinzu. Wir verifizieren ihn automatisch, sobald der Eintrag aktiv ist.', + typeLabel: 'Typ', + hostLabel: 'Host / Name', + valueLabel: 'Wert', }, }, - domainTaken: { - title: 'Diese Domain ({{domain}}) hat bereits eine SSO-Verbindung', - subtitle: - 'Wenden Sie sich an den Administrator der Anwendung, um über die bestehende Verbindung Zugriff zu erhalten.', - }, }, }, createOrganization: { diff --git a/packages/localizations/src/el-GR.ts b/packages/localizations/src/el-GR.ts index 0ae1f413953..593480afd13 100644 --- a/packages/localizations/src/el-GR.ts +++ b/packages/localizations/src/el-GR.ts @@ -196,30 +196,29 @@ export const elGR: LocalizationResource = { }, warning: 'Μόλις επιλεγεί ένας πάροχος δεν μπορείτε να τον αλλάξετε μέχρι να ολοκληρωθεί η ρύθμιση', }, - verifyEmailDomainStep: { - title: 'Επαλήθευση διεύθυνσης email', - subtitle: 'Επαληθεύστε τη διεύθυνση email στην οποία θέλετε να ενεργοποιήσετε τη σύνδεση επιχείρησης.', - addEmailAddress: { - formTitle: 'Χρειαζόμαστε το email σας', - formSubtitle: 'Για να ξεκινήσουμε, θα χρειαστούμε τη διεύθυνση email σας', - inputPlaceholder: 'name@company.com', - inputLabel: 'Διεύθυνση email', - }, - emailCode: { - formTitle: 'Επαληθεύστε τη διεύθυνση email σας', - formSubtitle: 'Εισαγάγετε τον κωδικό επαλήθευσης που στάλθηκε στο {{identifier}}', - resendButton: 'Δεν λάβατε κωδικό; Επαναποστολή', - verified: { - title: 'Λάβαμε το email σας', - subtitle: 'Έχετε επαληθεύσει τη διεύθυνση email σας με το ακόλουθο email', - inputLabel: 'Επαληθευμένη διεύθυνση email', + organizationDomainsStep: { + title: 'Προσθήκη τομέων SSO', + subtitle: 'Προσθέστε και επαληθεύστε την κυριότητα των τομέων που χρησιμοποιεί ο οργανισμός σας για σύνδεση.', + formFieldLabel__domain: 'Τομείς', + formFieldInputPlaceholder__domain: + 'Πληκτρολογήστε τον τομέα σας εδώ και κάντε κλικ στο «Προσθήκη» για να ξεκινήσετε', + formButtonPrimary__add: 'Προσθήκη', + domainSuggestion: { + messageLabel: 'Το email σας χρησιμοποιεί {{domain}}. Θέλετε να το προσθέσετε;', + formButtonPrimary__add: 'Προσθήκη {{domain}}', + }, + domainCard: { + badge__verified: 'Επαληθευμένο', + badge__unverified: 'Μη επαληθευμένο', + verifiedAtLabel: "Επαληθεύτηκε στις {{ date | shortDate('el-GR') }}", + txtRecord: { + instructions: + 'Προσθέστε αυτήν την εγγραφή TXT στον πάροχο DNS σας. Θα την επαληθεύσουμε αυτόματα μόλις η εγγραφή ενεργοποιηθεί.', + typeLabel: 'Τύπος', + hostLabel: 'Κεντρικός υπολογιστής / Όνομα', + valueLabel: 'Τιμή', }, }, - domainTaken: { - title: 'Αυτός ο τομέας ({{domain}}) διαθέτει ήδη σύνδεση SSO', - subtitle: - 'Επικοινωνήστε με τον διαχειριστή της εφαρμογής για να αποκτήσετε πρόσβαση μέσω της υπάρχουσας σύνδεσης.', - }, }, }, createOrganization: { diff --git a/packages/localizations/src/en-GB.ts b/packages/localizations/src/en-GB.ts index e3123b6333a..5027563902d 100644 --- a/packages/localizations/src/en-GB.ts +++ b/packages/localizations/src/en-GB.ts @@ -196,29 +196,27 @@ export const enGB: LocalizationResource = { }, warning: 'Once a provider is selected you cannot change again until the configuration is over', }, - verifyEmailDomainStep: { - title: 'Verify email address', - subtitle: 'Verify the domain you want to enable the enterprise connection on.', - addEmailAddress: { - formTitle: 'We need your email', - formSubtitle: 'In order to start we will need your email address', - inputPlaceholder: 'name@company.com', - inputLabel: 'Email address', - }, - emailCode: { - formTitle: 'Verify your email address', - formSubtitle: 'Enter the verification code sent to {{identifier}}', - resendButton: "Didn't receive a code? Resend", - verified: { - title: 'We got your email', - subtitle: "You've verified your email address with the following email", - inputLabel: 'Verified email address', + organizationDomainsStep: { + title: 'Add SSO domains', + subtitle: 'Add and verify ownership of the domains your organisation uses to sign in.', + formFieldLabel__domain: 'Domains', + formFieldInputPlaceholder__domain: 'Type your domain here and click add to start', + formButtonPrimary__add: 'Add', + domainSuggestion: { + messageLabel: 'Your email uses {{domain}}. Do you want to add it?', + formButtonPrimary__add: 'Add {{domain}}', + }, + domainCard: { + badge__verified: 'Verified', + badge__unverified: 'Unverified', + verifiedAtLabel: "Verified on {{ date | shortDate('en-GB') }}", + txtRecord: { + instructions: "Add this TXT record to your DNS provider. We'll verify automatically once the record is live.", + typeLabel: 'Type', + hostLabel: 'Host / Name', + valueLabel: 'Value', }, }, - domainTaken: { - title: 'This domain ({{domain}}) already has an SSO connection', - subtitle: "Contact the application's administrator to get access through the existing connection.", - }, }, }, createOrganization: { diff --git a/packages/localizations/src/en-US.ts b/packages/localizations/src/en-US.ts index b6d14fad930..bc85f43fec2 100644 --- a/packages/localizations/src/en-US.ts +++ b/packages/localizations/src/en-US.ts @@ -269,29 +269,27 @@ export const enUS: LocalizationResource = { }, warning: 'Once a provider is selected you cannot change again until the configuration is over', }, - verifyEmailDomainStep: { - title: 'Verify email address', - subtitle: 'Verify the domain you want to enable the enterprise connection on.', - addEmailAddress: { - formTitle: 'We need your email', - formSubtitle: 'In order to start we will need your email address', - inputPlaceholder: 'name@company.com', - inputLabel: 'Email address', - }, - emailCode: { - formTitle: 'Verify your email address', - formSubtitle: 'Enter the verification code sent to {{identifier}}', - resendButton: "Didn't receive a code? Resend", - verified: { - title: 'We got your email', - subtitle: "You've verified your email address with the following email", - inputLabel: 'Verified email address', + organizationDomainsStep: { + title: 'Add SSO domains', + subtitle: 'Add and verify ownership of the domains your organization uses to sign in.', + formFieldLabel__domain: 'Domains', + formFieldInputPlaceholder__domain: 'Type your domain here and click add to start', + formButtonPrimary__add: 'Add', + domainSuggestion: { + messageLabel: 'Your email uses {{domain}}. Do you want to add it?', + formButtonPrimary__add: 'Add {{domain}}', + }, + domainCard: { + badge__verified: 'Verified', + badge__unverified: 'Unverified', + verifiedAtLabel: "Verified on {{ date | shortDate('en-US') }}", + txtRecord: { + instructions: "Add this TXT record to your DNS provider. We'll verify automatically once the record is live.", + typeLabel: 'Type', + hostLabel: 'Host / Name', + valueLabel: 'Value', }, }, - domainTaken: { - title: 'This domain ({{domain}}) already has an SSO connection', - subtitle: "Contact the application's administrator to get access through the existing connection.", - }, }, testConfigurationStep: { title: 'Test your SSO connection', @@ -882,6 +880,7 @@ export const enUS: LocalizationResource = { badge__automaticInvitation: 'Automatic invitations', badge__automaticSuggestion: 'Automatic suggestions', badge__manualInvitation: 'No automatic enrollment', + badge__enterpriseSso: 'Enterprise SSO', badge__unverified: 'Unverified', billingPage: { paymentHistorySection: { diff --git a/packages/localizations/src/es-CR.ts b/packages/localizations/src/es-CR.ts index 5c193ce8b3d..59cb4db9664 100644 --- a/packages/localizations/src/es-CR.ts +++ b/packages/localizations/src/es-CR.ts @@ -196,29 +196,28 @@ export const esCR: LocalizationResource = { }, warning: 'Una vez que se selecciona un proveedor no puedes cambiarlo hasta que termine la configuración', }, - verifyEmailDomainStep: { - title: 'Verificar correo electrónico', - subtitle: 'Verifica la dirección de correo electrónico en la que deseas habilitar la conexión empresarial.', - addEmailAddress: { - formTitle: 'Necesitamos tu correo electrónico', - formSubtitle: 'Para empezar, necesitaremos tu dirección de correo electrónico', - inputPlaceholder: 'name@company.com', - inputLabel: 'Dirección de correo electrónico', - }, - emailCode: { - formTitle: 'Verifica tu dirección de correo electrónico', - formSubtitle: 'Ingresa el código de verificación enviado a {{identifier}}', - resendButton: '¿No recibiste un código? Reenviar', - verified: { - title: 'Recibimos tu correo electrónico', - subtitle: 'Has verificado tu dirección de correo electrónico con el siguiente correo', - inputLabel: 'Dirección de correo electrónico verificada', + organizationDomainsStep: { + title: 'Agregar dominios SSO', + subtitle: 'Agrega y verifica la propiedad de los dominios que tu organización usa para iniciar sesión.', + formFieldLabel__domain: 'Dominios', + formFieldInputPlaceholder__domain: 'Escribe aquí tu dominio y haz clic en Agregar para empezar', + formButtonPrimary__add: 'Agregar', + domainSuggestion: { + messageLabel: 'Tu correo electrónico usa {{domain}}. ¿Quieres agregarlo?', + formButtonPrimary__add: 'Agregar {{domain}}', + }, + domainCard: { + badge__verified: 'Verificado', + badge__unverified: 'Sin verificar', + verifiedAtLabel: "Verificado el {{ date | shortDate('es-CR') }}", + txtRecord: { + instructions: + 'Agrega este registro TXT a tu proveedor de DNS. Lo verificaremos automáticamente una vez que el registro esté activo.', + typeLabel: 'Tipo', + hostLabel: 'Host / Nombre', + valueLabel: 'Valor', }, }, - domainTaken: { - title: 'Este dominio ({{domain}}) ya tiene una conexión SSO', - subtitle: 'Contacta al administrador de la aplicación para obtener acceso a través de la conexión existente.', - }, }, }, createOrganization: { diff --git a/packages/localizations/src/es-ES.ts b/packages/localizations/src/es-ES.ts index 2ffb8d21c1b..1b112612573 100644 --- a/packages/localizations/src/es-ES.ts +++ b/packages/localizations/src/es-ES.ts @@ -202,30 +202,28 @@ export const esES: LocalizationResource = { }, warning: 'Una vez seleccionado un proveedor no podrás cambiarlo hasta que finalice la configuración', }, - verifyEmailDomainStep: { - title: 'Verificar correo electrónico', - subtitle: 'Verifica la dirección de correo electrónico en la que deseas habilitar la conexión empresarial.', - addEmailAddress: { - formTitle: 'Necesitamos tu correo electrónico', - formSubtitle: 'Para empezar, necesitaremos tu dirección de correo electrónico', - inputPlaceholder: 'name@company.com', - inputLabel: 'Dirección de correo electrónico', - }, - emailCode: { - formTitle: 'Verifica tu dirección de correo electrónico', - formSubtitle: 'Introduce el código de verificación enviado a {{identifier}}', - resendButton: '¿No has recibido un código? Reenviar', - verified: { - title: 'Hemos recibido tu correo electrónico', - subtitle: 'Has verificado tu dirección de correo electrónico con el siguiente correo', - inputLabel: 'Dirección de correo electrónico verificada', + organizationDomainsStep: { + title: 'Añadir dominios SSO', + subtitle: 'Añade y verifica la propiedad de los dominios que tu organización usa para iniciar sesión.', + formFieldLabel__domain: 'Dominios', + formFieldInputPlaceholder__domain: 'Escribe aquí tu dominio y haz clic en Añadir para empezar', + formButtonPrimary__add: 'Añadir', + domainSuggestion: { + messageLabel: 'Tu correo electrónico usa {{domain}}. ¿Quieres añadirlo?', + formButtonPrimary__add: 'Añadir {{domain}}', + }, + domainCard: { + badge__verified: 'Verificado', + badge__unverified: 'Sin verificar', + verifiedAtLabel: "Verificado el {{ date | shortDate('es-ES') }}", + txtRecord: { + instructions: + 'Añade este registro TXT a tu proveedor de DNS. Lo verificaremos automáticamente una vez que el registro esté activo.', + typeLabel: 'Tipo', + hostLabel: 'Host / Nombre', + valueLabel: 'Valor', }, }, - domainTaken: { - title: 'Este dominio ({{domain}}) ya tiene una conexión SSO', - subtitle: - 'Contacta con el administrador de la aplicación para obtener acceso a través de la conexión existente.', - }, }, }, createOrganization: { diff --git a/packages/localizations/src/es-MX.ts b/packages/localizations/src/es-MX.ts index 1b1fa64ac67..bbafd7f18ff 100644 --- a/packages/localizations/src/es-MX.ts +++ b/packages/localizations/src/es-MX.ts @@ -197,29 +197,28 @@ export const esMX: LocalizationResource = { }, warning: 'Una vez que se selecciona un proveedor no puedes cambiarlo hasta que termine la configuración', }, - verifyEmailDomainStep: { - title: 'Verificar correo electrónico', - subtitle: 'Verifica la dirección de correo electrónico en la que deseas habilitar la conexión empresarial.', - addEmailAddress: { - formTitle: 'Necesitamos tu correo electrónico', - formSubtitle: 'Para empezar, necesitaremos tu dirección de correo electrónico', - inputPlaceholder: 'name@company.com', - inputLabel: 'Dirección de correo electrónico', - }, - emailCode: { - formTitle: 'Verifica tu dirección de correo electrónico', - formSubtitle: 'Ingresa el código de verificación enviado a {{identifier}}', - resendButton: '¿No recibiste un código? Reenviar', - verified: { - title: 'Recibimos tu correo electrónico', - subtitle: 'Has verificado tu dirección de correo electrónico con el siguiente correo', - inputLabel: 'Dirección de correo electrónico verificada', + organizationDomainsStep: { + title: 'Agregar dominios SSO', + subtitle: 'Agrega y verifica la propiedad de los dominios que tu organización usa para iniciar sesión.', + formFieldLabel__domain: 'Dominios', + formFieldInputPlaceholder__domain: 'Escribe aquí tu dominio y haz clic en Agregar para empezar', + formButtonPrimary__add: 'Agregar', + domainSuggestion: { + messageLabel: 'Tu correo electrónico usa {{domain}}. ¿Quieres agregarlo?', + formButtonPrimary__add: 'Agregar {{domain}}', + }, + domainCard: { + badge__verified: 'Verificado', + badge__unverified: 'Sin verificar', + verifiedAtLabel: "Verificado el {{ date | shortDate('es-MX') }}", + txtRecord: { + instructions: + 'Agrega este registro TXT a tu proveedor de DNS. Lo verificaremos automáticamente una vez que el registro esté activo.', + typeLabel: 'Tipo', + hostLabel: 'Host / Nombre', + valueLabel: 'Valor', }, }, - domainTaken: { - title: 'Este dominio ({{domain}}) ya tiene una conexión SSO', - subtitle: 'Contacta al administrador de la aplicación para obtener acceso a través de la conexión existente.', - }, }, }, createOrganization: { diff --git a/packages/localizations/src/es-UY.ts b/packages/localizations/src/es-UY.ts index bd16067ed07..9ae86e39710 100644 --- a/packages/localizations/src/es-UY.ts +++ b/packages/localizations/src/es-UY.ts @@ -196,29 +196,28 @@ export const esUY: LocalizationResource = { }, warning: 'Una vez que se selecciona un proveedor no podés cambiarlo hasta que termine la configuración', }, - verifyEmailDomainStep: { - title: 'Verificar correo electrónico', - subtitle: 'Verificá la dirección de correo electrónico en la que querés habilitar la conexión empresarial.', - addEmailAddress: { - formTitle: 'Necesitamos tu correo electrónico', - formSubtitle: 'Para empezar, necesitaremos tu dirección de correo electrónico', - inputPlaceholder: 'name@company.com', - inputLabel: 'Dirección de correo electrónico', - }, - emailCode: { - formTitle: 'Verificá tu dirección de correo electrónico', - formSubtitle: 'Ingresá el código de verificación enviado a {{identifier}}', - resendButton: '¿No recibiste un código? Reenviar', - verified: { - title: 'Recibimos tu correo electrónico', - subtitle: 'Verificaste tu dirección de correo electrónico con el siguiente correo', - inputLabel: 'Dirección de correo electrónico verificada', + organizationDomainsStep: { + title: 'Agregar dominios SSO', + subtitle: 'Agregá y verificá la propiedad de los dominios que tu organización usa para iniciar sesión.', + formFieldLabel__domain: 'Dominios', + formFieldInputPlaceholder__domain: 'Escribí aquí tu dominio y hacé clic en Agregar para empezar', + formButtonPrimary__add: 'Agregar', + domainSuggestion: { + messageLabel: 'Tu correo electrónico usa {{domain}}. ¿Querés agregarlo?', + formButtonPrimary__add: 'Agregar {{domain}}', + }, + domainCard: { + badge__verified: 'Verificado', + badge__unverified: 'Sin verificar', + verifiedAtLabel: "Verificado el {{ date | shortDate('es-UY') }}", + txtRecord: { + instructions: + 'Agregá este registro TXT a tu proveedor de DNS. Lo verificaremos automáticamente una vez que el registro esté activo.', + typeLabel: 'Tipo', + hostLabel: 'Host / Nombre', + valueLabel: 'Valor', }, }, - domainTaken: { - title: 'Este dominio ({{domain}}) ya tiene una conexión SSO', - subtitle: 'Contactá al administrador de la aplicación para obtener acceso a través de la conexión existente.', - }, }, }, createOrganization: { diff --git a/packages/localizations/src/fa-IR.ts b/packages/localizations/src/fa-IR.ts index 16b8afa5448..87c3c745765 100644 --- a/packages/localizations/src/fa-IR.ts +++ b/packages/localizations/src/fa-IR.ts @@ -201,29 +201,28 @@ export const faIR: LocalizationResource = { }, warning: 'پس از انتخاب یک ارائه‌دهنده، نمی‌توانید آن را تا پایان پیکربندی تغییر دهید', }, - verifyEmailDomainStep: { - title: 'تأیید آدرس ایمیل', - subtitle: 'آدرس ایمیلی را که می‌خواهید اتصال سازمانی روی آن فعال شود، تأیید کنید.', - addEmailAddress: { - formTitle: 'به ایمیل شما نیاز داریم', - formSubtitle: 'برای شروع به آدرس ایمیل شما نیاز داریم', - inputPlaceholder: 'name@company.com', - inputLabel: 'آدرس ایمیل', - }, - emailCode: { - formTitle: 'آدرس ایمیل خود را تأیید کنید', - formSubtitle: 'کد تأیید ارسال شده به {{identifier}} را وارد کنید', - resendButton: 'کد را دریافت نکردید؟ ارسال مجدد', - verified: { - title: 'ایمیل شما را دریافت کردیم', - subtitle: 'شما آدرس ایمیل خود را با ایمیل زیر تأیید کرده‌اید', - inputLabel: 'آدرس ایمیل تأیید شده', + organizationDomainsStep: { + title: 'افزودن دامنه‌های SSO', + subtitle: 'مالکیت دامنه‌هایی را که سازمان شما برای ورود استفاده می‌کند، اضافه و تأیید کنید.', + formFieldLabel__domain: 'دامنه‌ها', + formFieldInputPlaceholder__domain: 'دامنه خود را اینجا تایپ کنید و برای شروع روی افزودن کلیک کنید', + formButtonPrimary__add: 'افزودن', + domainSuggestion: { + messageLabel: 'ایمیل شما از {{domain}} استفاده می‌کند. آیا می‌خواهید آن را اضافه کنید؟', + formButtonPrimary__add: 'افزودن {{domain}}', + }, + domainCard: { + badge__verified: 'تأییدشده', + badge__unverified: 'تأییدنشده', + verifiedAtLabel: "تأیید‌شده در {{ date | shortDate('fa-IR') }}", + txtRecord: { + instructions: + 'این رکورد TXT را به ارائه‌دهنده DNS خود اضافه کنید. به‌محض فعال شدن رکورد، به‌طور خودکار آن را تأیید می‌کنیم.', + typeLabel: 'نوع', + hostLabel: 'میزبان / نام', + valueLabel: 'مقدار', }, }, - domainTaken: { - title: 'این دامنه ({{domain}}) قبلاً یک اتصال SSO دارد', - subtitle: 'برای دسترسی از طریق اتصال موجود، با مدیر برنامه تماس بگیرید.', - }, }, }, createOrganization: { diff --git a/packages/localizations/src/fi-FI.ts b/packages/localizations/src/fi-FI.ts index 7f5fa9eeb14..2bd8048df77 100644 --- a/packages/localizations/src/fi-FI.ts +++ b/packages/localizations/src/fi-FI.ts @@ -224,30 +224,28 @@ export const fiFI: LocalizationResource = { }, warning: 'Kun palveluntarjoaja on valittu, et voi vaihtaa sitä ennen kuin määritys on valmis', }, - verifyEmailDomainStep: { - title: 'Vahvista sähköpostiosoite', - subtitle: 'Vahvista sähköpostiosoite, jolle haluat ottaa yritysyhteyden käyttöön.', - addEmailAddress: { - formTitle: 'Tarvitsemme sähköpostiosi', - formSubtitle: 'Aloittaaksemme tarvitsemme sähköpostiosoitteesi', - inputPlaceholder: 'name@company.com', - inputLabel: 'Sähköpostiosoite', - }, - emailCode: { - formTitle: 'Vahvista sähköpostiosoitteesi', - formSubtitle: 'Anna vahvistuskoodi, joka lähetettiin osoitteeseen {{identifier}}', - resendButton: 'Etkö saanut koodia? Lähetä uudelleen', - verified: { - title: 'Saimme sähköpostisi', - subtitle: 'Olet vahvistanut sähköpostiosoitteesi seuraavalla sähköpostilla', - inputLabel: 'Vahvistettu sähköpostiosoite', + organizationDomainsStep: { + title: 'Lisää SSO-verkkotunnuksia', + subtitle: 'Lisää ja vahvista niiden verkkotunnusten omistajuus, joita organisaatiosi käyttää kirjautumiseen.', + formFieldLabel__domain: 'Verkkotunnukset', + formFieldInputPlaceholder__domain: 'Kirjoita verkkotunnuksesi tähän ja aloita napsauttamalla Lisää', + formButtonPrimary__add: 'Lisää', + domainSuggestion: { + messageLabel: 'Sähköpostisi käyttää osoitetta {{domain}}. Haluatko lisätä sen?', + formButtonPrimary__add: 'Lisää {{domain}}', + }, + domainCard: { + badge__verified: 'Vahvistettu', + badge__unverified: 'Vahvistamaton', + verifiedAtLabel: "Vahvistettu {{ date | shortDate('fi-FI') }}", + txtRecord: { + instructions: + 'Lisää tämä TXT-tietue DNS-palveluntarjoajallesi. Vahvistamme sen automaattisesti, kun tietue on aktiivinen.', + typeLabel: 'Tyyppi', + hostLabel: 'Isäntä / Nimi', + valueLabel: 'Arvo', }, }, - domainTaken: { - title: 'Tällä verkkotunnuksella ({{domain}}) on jo SSO-yhteys', - subtitle: - 'Ota yhteyttä sovelluksen järjestelmänvalvojaan saadaksesi käyttöoikeudet olemassa olevan yhteyden kautta.', - }, }, }, createOrganization: { diff --git a/packages/localizations/src/fr-FR.ts b/packages/localizations/src/fr-FR.ts index a33f1289508..9e1f79e699c 100644 --- a/packages/localizations/src/fr-FR.ts +++ b/packages/localizations/src/fr-FR.ts @@ -205,29 +205,28 @@ export const frFR: LocalizationResource = { warning: "Une fois un fournisseur sélectionné, vous ne pourrez plus en changer jusqu'à la fin de la configuration", }, - verifyEmailDomainStep: { - title: "Vérifier l'adresse e-mail", - subtitle: "Vérifiez l'adresse e-mail sur laquelle vous souhaitez activer la connexion entreprise.", - addEmailAddress: { - formTitle: 'Nous avons besoin de votre e-mail', - formSubtitle: 'Pour commencer, nous aurons besoin de votre adresse e-mail', - inputPlaceholder: 'name@company.com', - inputLabel: 'Adresse e-mail', - }, - emailCode: { - formTitle: 'Vérifiez votre adresse e-mail', - formSubtitle: 'Entrez le code de vérification envoyé à {{identifier}}', - resendButton: "Vous n'avez pas reçu de code ? Renvoyer", - verified: { - title: 'Nous avons reçu votre e-mail', - subtitle: "Vous avez vérifié votre adresse e-mail avec l'e-mail suivant", - inputLabel: 'Adresse e-mail vérifiée', + organizationDomainsStep: { + title: 'Ajouter des domaines SSO', + subtitle: 'Ajoutez et vérifiez la propriété des domaines que votre organisation utilise pour se connecter.', + formFieldLabel__domain: 'Domaines', + formFieldInputPlaceholder__domain: 'Saisissez votre domaine ici et cliquez sur Ajouter pour commencer', + formButtonPrimary__add: 'Ajouter', + domainSuggestion: { + messageLabel: 'Votre e-mail utilise {{domain}}. Voulez-vous l’ajouter ?', + formButtonPrimary__add: 'Ajouter {{domain}}', + }, + domainCard: { + badge__verified: 'Vérifié', + badge__unverified: 'Non vérifié', + verifiedAtLabel: "Vérifié le {{ date | shortDate('fr-FR') }}", + txtRecord: { + instructions: + 'Ajoutez cet enregistrement TXT à votre fournisseur DNS. Nous le vérifierons automatiquement une fois l’enregistrement actif.', + typeLabel: 'Type', + hostLabel: 'Hôte / Nom', + valueLabel: 'Valeur', }, }, - domainTaken: { - title: "Ce domaine ({{domain}}) dispose déjà d'une connexion SSO", - subtitle: "Contactez l'administrateur de l'application pour obtenir l'accès via la connexion existante.", - }, }, }, createOrganization: { diff --git a/packages/localizations/src/he-IL.ts b/packages/localizations/src/he-IL.ts index 6dbf545ab7e..edfb2dde7df 100644 --- a/packages/localizations/src/he-IL.ts +++ b/packages/localizations/src/he-IL.ts @@ -196,29 +196,27 @@ export const heIL: LocalizationResource = { }, warning: 'לאחר בחירת ספק לא ניתן לשנות אותו עד לסיום ההגדרה', }, - verifyEmailDomainStep: { - title: 'אימות כתובת אימייל', - subtitle: 'אמת את כתובת האימייל שעליה ברצונך להפעיל את חיבור הארגון.', - addEmailAddress: { - formTitle: 'אנחנו צריכים את האימייל שלך', - formSubtitle: 'כדי להתחיל, נצטרך את כתובת האימייל שלך', - inputPlaceholder: 'name@company.com', - inputLabel: 'כתובת אימייל', - }, - emailCode: { - formTitle: 'אמת את כתובת האימייל שלך', - formSubtitle: 'הזן את קוד האימות שנשלח אל {{identifier}}', - resendButton: 'לא קיבלת קוד? שלח שוב', - verified: { - title: 'קיבלנו את האימייל שלך', - subtitle: 'אימתת את כתובת האימייל שלך עם האימייל הבא', - inputLabel: 'כתובת אימייל מאומתת', + organizationDomainsStep: { + title: 'הוספת דומיינים של SSO', + subtitle: 'הוסף ואמת בעלות על הדומיינים שהארגון שלך משתמש בהם כדי להתחבר.', + formFieldLabel__domain: 'דומיינים', + formFieldInputPlaceholder__domain: 'הקלד את הדומיין שלך כאן ולחץ על הוסף כדי להתחיל', + formButtonPrimary__add: 'הוסף', + domainSuggestion: { + messageLabel: 'האימייל שלך משתמש ב-{{domain}}. האם ברצונך להוסיף אותו?', + formButtonPrimary__add: 'הוסף {{domain}}', + }, + domainCard: { + badge__verified: 'מאומת', + badge__unverified: 'לא מאומת', + verifiedAtLabel: "אומת בתאריך {{ date | shortDate('he-IL') }}", + txtRecord: { + instructions: 'הוסף רשומת TXT זו אצל ספק ה-DNS שלך. נאמת אותה אוטומטית ברגע שהרשומה תהיה פעילה.', + typeLabel: 'סוג', + hostLabel: 'מארח / שם', + valueLabel: 'ערך', }, }, - domainTaken: { - title: 'לדומיין הזה ({{domain}}) כבר יש חיבור SSO', - subtitle: 'צור קשר עם מנהל היישום כדי לקבל גישה דרך החיבור הקיים.', - }, }, }, createOrganization: { diff --git a/packages/localizations/src/hi-IN.ts b/packages/localizations/src/hi-IN.ts index 66a57251752..6e4397ae217 100644 --- a/packages/localizations/src/hi-IN.ts +++ b/packages/localizations/src/hi-IN.ts @@ -202,29 +202,28 @@ export const hiIN: LocalizationResource = { }, warning: 'एक बार प्रदाता का चयन करने के बाद आप कॉन्फ़िगरेशन समाप्त होने तक इसे बदल नहीं सकते', }, - verifyEmailDomainStep: { - title: 'ईमेल पता सत्यापित करें', - subtitle: 'उस ईमेल पते को सत्यापित करें जिस पर आप एंटरप्राइज़ कनेक्शन सक्षम करना चाहते हैं।', - addEmailAddress: { - formTitle: 'हमें आपके ईमेल की आवश्यकता है', - formSubtitle: 'शुरू करने के लिए हमें आपके ईमेल पते की आवश्यकता होगी', - inputPlaceholder: 'name@company.com', - inputLabel: 'ईमेल पता', - }, - emailCode: { - formTitle: 'अपना ईमेल पता सत्यापित करें', - formSubtitle: '{{identifier}} पर भेजा गया सत्यापन कोड दर्ज करें', - resendButton: 'कोड नहीं मिला? पुनः भेजें', - verified: { - title: 'हमें आपका ईमेल मिल गया', - subtitle: 'आपने निम्नलिखित ईमेल के साथ अपना ईमेल पता सत्यापित किया है', - inputLabel: 'सत्यापित ईमेल पता', + organizationDomainsStep: { + title: 'SSO डोमेन जोड़ें', + subtitle: 'अपने संगठन द्वारा साइन इन के लिए उपयोग किए जाने वाले डोमेन का स्वामित्व जोड़ें और सत्यापित करें।', + formFieldLabel__domain: 'डोमेन', + formFieldInputPlaceholder__domain: 'अपना डोमेन यहाँ टाइप करें और शुरू करने के लिए जोड़ें पर क्लिक करें', + formButtonPrimary__add: 'जोड़ें', + domainSuggestion: { + messageLabel: 'आपका ईमेल {{domain}} का उपयोग करता है। क्या आप इसे जोड़ना चाहते हैं?', + formButtonPrimary__add: '{{domain}} जोड़ें', + }, + domainCard: { + badge__verified: 'सत्यापित', + badge__unverified: 'असत्यापित', + verifiedAtLabel: "{{ date | shortDate('hi-IN') }} को सत्यापित", + txtRecord: { + instructions: + 'इस TXT रिकॉर्ड को अपने DNS प्रदाता में जोड़ें। रिकॉर्ड सक्रिय होते ही हम स्वतः सत्यापित कर देंगे।', + typeLabel: 'प्रकार', + hostLabel: 'होस्ट / नाम', + valueLabel: 'मान', }, }, - domainTaken: { - title: 'इस डोमेन ({{domain}}) में पहले से ही एक SSO कनेक्शन है', - subtitle: 'मौजूदा कनेक्शन के माध्यम से एक्सेस प्राप्त करने के लिए एप्लिकेशन के व्यवस्थापक से संपर्क करें।', - }, }, }, createOrganization: { diff --git a/packages/localizations/src/hr-HR.ts b/packages/localizations/src/hr-HR.ts index 1b31cfebbfa..e967ebad9d4 100644 --- a/packages/localizations/src/hr-HR.ts +++ b/packages/localizations/src/hr-HR.ts @@ -225,29 +225,28 @@ export const hrHR: LocalizationResource = { }, warning: 'Nakon odabira pružatelja ne možete ga ponovno mijenjati dok konfiguracija ne završi', }, - verifyEmailDomainStep: { - title: 'Potvrdi e-mail adresu', - subtitle: 'Potvrdite e-mail adresu na kojoj želite omogućiti poslovnu vezu.', - addEmailAddress: { - formTitle: 'Treba nam vaš e-mail', - formSubtitle: 'Za početak ćemo trebati vašu e-mail adresu', - inputPlaceholder: 'name@company.com', - inputLabel: 'E-mail adresa', - }, - emailCode: { - formTitle: 'Potvrdite svoju e-mail adresu', - formSubtitle: 'Unesite verifikacijski kod poslan na {{identifier}}', - resendButton: 'Niste primili kod? Pošalji ponovno', - verified: { - title: 'Primili smo vaš e-mail', - subtitle: 'Potvrdili ste svoju e-mail adresu sljedećim e-mailom', - inputLabel: 'Potvrđena e-mail adresa', + organizationDomainsStep: { + title: 'Dodaj SSO domene', + subtitle: 'Dodajte i potvrdite vlasništvo nad domenama koje vaša organizacija koristi za prijavu.', + formFieldLabel__domain: 'Domene', + formFieldInputPlaceholder__domain: 'Ovdje upišite svoju domenu i kliknite dodaj za početak', + formButtonPrimary__add: 'Dodaj', + domainSuggestion: { + messageLabel: 'Vaša e-pošta koristi {{domain}}. Želite li ga dodati?', + formButtonPrimary__add: 'Dodaj {{domain}}', + }, + domainCard: { + badge__verified: 'Potvrđeno', + badge__unverified: 'Nepotvrđeno', + verifiedAtLabel: "Potvrđeno {{ date | shortDate('hr-HR') }}", + txtRecord: { + instructions: + 'Dodajte ovaj TXT zapis kod svog DNS pružatelja. Automatski ćemo ga potvrditi čim zapis postane aktivan.', + typeLabel: 'Vrsta', + hostLabel: 'Host / Naziv', + valueLabel: 'Vrijednost', }, }, - domainTaken: { - title: 'Ova domena ({{domain}}) već ima SSO vezu', - subtitle: 'Kontaktirajte administratora aplikacije kako biste dobili pristup putem postojeće veze.', - }, }, }, createOrganization: { diff --git a/packages/localizations/src/hu-HU.ts b/packages/localizations/src/hu-HU.ts index 3714f35e4a0..b0366ef86eb 100644 --- a/packages/localizations/src/hu-HU.ts +++ b/packages/localizations/src/hu-HU.ts @@ -225,29 +225,29 @@ export const huHU: LocalizationResource = { }, warning: 'Miután kiválasztotta a szolgáltatót, nem módosíthatja, amíg a konfiguráció be nem fejeződik', }, - verifyEmailDomainStep: { - title: 'E-mail-cím megerősítése', - subtitle: 'Erősítse meg azt az e-mail-címet, amelyen engedélyezni szeretné a vállalati kapcsolatot.', - addEmailAddress: { - formTitle: 'Szükségünk van az e-mail-címére', - formSubtitle: 'A kezdéshez szükségünk lesz az e-mail-címére', - inputPlaceholder: 'name@company.com', - inputLabel: 'E-mail-cím', - }, - emailCode: { - formTitle: 'Erősítse meg az e-mail-címét', - formSubtitle: 'Adja meg a {{identifier}} címre küldött ellenőrző kódot', - resendButton: 'Nem kapott kódot? Küldés újra', - verified: { - title: 'Megkaptuk az e-mailjét', - subtitle: 'Megerősítette az e-mail-címét az alábbi e-maillel', - inputLabel: 'Megerősített e-mail-cím', + organizationDomainsStep: { + title: 'SSO-tartományok hozzáadása', + subtitle: + 'Adja hozzá és igazolja azon tartományok tulajdonjogát, amelyeket szervezete a bejelentkezéshez használ.', + formFieldLabel__domain: 'Tartományok', + formFieldInputPlaceholder__domain: 'Írja be ide a tartományát, majd kattintson a Hozzáadás gombra a kezdéshez', + formButtonPrimary__add: 'Hozzáadás', + domainSuggestion: { + messageLabel: 'Az e-mail-címe a következőt használja: {{domain}}. Szeretné hozzáadni?', + formButtonPrimary__add: '{{domain}} hozzáadása', + }, + domainCard: { + badge__verified: 'Igazolva', + badge__unverified: 'Nincs igazolva', + verifiedAtLabel: "Igazolva: {{ date | shortDate('hu-HU') }}", + txtRecord: { + instructions: + 'Adja hozzá ezt a TXT-rekordot a DNS-szolgáltatójához. Automatikusan igazoljuk, amint a rekord aktívvá válik.', + typeLabel: 'Típus', + hostLabel: 'Gazda / Név', + valueLabel: 'Érték', }, }, - domainTaken: { - title: 'Ez a domain ({{domain}}) már rendelkezik SSO-kapcsolattal', - subtitle: 'A meglévő kapcsolaton keresztüli hozzáférés érdekében forduljon az alkalmazás rendszergazdájához.', - }, }, }, createOrganization: { diff --git a/packages/localizations/src/id-ID.ts b/packages/localizations/src/id-ID.ts index 1483a6307e2..4fd1599de2e 100644 --- a/packages/localizations/src/id-ID.ts +++ b/packages/localizations/src/id-ID.ts @@ -196,29 +196,28 @@ export const idID: LocalizationResource = { }, warning: 'Setelah penyedia dipilih, Anda tidak dapat mengubahnya lagi sampai konfigurasi selesai', }, - verifyEmailDomainStep: { - title: 'Verifikasi alamat email', - subtitle: 'Verifikasi alamat email yang ingin Anda aktifkan koneksi enterprise-nya.', - addEmailAddress: { - formTitle: 'Kami membutuhkan email Anda', - formSubtitle: 'Untuk memulai, kami membutuhkan alamat email Anda', - inputPlaceholder: 'name@company.com', - inputLabel: 'Alamat email', - }, - emailCode: { - formTitle: 'Verifikasi alamat email Anda', - formSubtitle: 'Masukkan kode verifikasi yang dikirim ke {{identifier}}', - resendButton: 'Tidak menerima kode? Kirim ulang', - verified: { - title: 'Kami mendapatkan email Anda', - subtitle: 'Anda telah memverifikasi alamat email Anda dengan email berikut', - inputLabel: 'Alamat email terverifikasi', + organizationDomainsStep: { + title: 'Tambahkan domain SSO', + subtitle: 'Tambahkan dan verifikasi kepemilikan domain yang digunakan organisasi Anda untuk masuk.', + formFieldLabel__domain: 'Domain', + formFieldInputPlaceholder__domain: 'Ketik domain Anda di sini dan klik tambah untuk memulai', + formButtonPrimary__add: 'Tambah', + domainSuggestion: { + messageLabel: 'Email Anda menggunakan {{domain}}. Apakah Anda ingin menambahkannya?', + formButtonPrimary__add: 'Tambah {{domain}}', + }, + domainCard: { + badge__verified: 'Terverifikasi', + badge__unverified: 'Belum terverifikasi', + verifiedAtLabel: "Diverifikasi pada {{ date | shortDate('id-ID') }}", + txtRecord: { + instructions: + 'Tambahkan data TXT ini ke penyedia DNS Anda. Kami akan memverifikasi secara otomatis setelah data aktif.', + typeLabel: 'Tipe', + hostLabel: 'Host / Nama', + valueLabel: 'Nilai', }, }, - domainTaken: { - title: 'Domain ini ({{domain}}) sudah memiliki koneksi SSO', - subtitle: 'Hubungi administrator aplikasi untuk mendapatkan akses melalui koneksi yang ada.', - }, }, }, createOrganization: { diff --git a/packages/localizations/src/is-IS.ts b/packages/localizations/src/is-IS.ts index 304827137bb..23346c6dbeb 100644 --- a/packages/localizations/src/is-IS.ts +++ b/packages/localizations/src/is-IS.ts @@ -224,29 +224,28 @@ export const isIS: LocalizationResource = { }, warning: 'Þegar þjónustuaðili hefur verið valinn er ekki hægt að breyta aftur fyrr en stillingu er lokið', }, - verifyEmailDomainStep: { - title: 'Staðfesta tölvupóstfang', - subtitle: 'Staðfestu tölvupóstfangið sem þú vilt virkja fyrirtækjatenginguna á.', - addEmailAddress: { - formTitle: 'Við þurfum tölvupóstinn þinn', - formSubtitle: 'Til að byrja þurfum við tölvupóstfangið þitt', - inputPlaceholder: 'name@company.com', - inputLabel: 'Tölvupóstfang', - }, - emailCode: { - formTitle: 'Staðfestu tölvupóstfangið þitt', - formSubtitle: 'Sláðu inn staðfestingarkóðann sem var sendur á {{identifier}}', - resendButton: 'Fékkstu engan kóða? Senda aftur', - verified: { - title: 'Við fengum tölvupóstinn þinn', - subtitle: 'Þú hefur staðfest tölvupóstfangið þitt með eftirfarandi tölvupósti', - inputLabel: 'Staðfest tölvupóstfang', + organizationDomainsStep: { + title: 'Bæta við SSO-lénum', + subtitle: 'Bættu við og staðfestu eignarhald á lénunum sem fyrirtækið þitt notar til að skrá sig inn.', + formFieldLabel__domain: 'Lén', + formFieldInputPlaceholder__domain: 'Skrifaðu lénið þitt hér og smelltu á bæta við til að byrja', + formButtonPrimary__add: 'Bæta við', + domainSuggestion: { + messageLabel: 'Tölvupósturinn þinn notar {{domain}}. Viltu bæta því við?', + formButtonPrimary__add: 'Bæta við {{domain}}', + }, + domainCard: { + badge__verified: 'Staðfest', + badge__unverified: 'Óstaðfest', + verifiedAtLabel: "Staðfest {{ date | shortDate('is-IS') }}", + txtRecord: { + instructions: + 'Bættu þessari TXT-færslu við DNS-þjónustuna þína. Við staðfestum sjálfkrafa um leið og færslan verður virk.', + typeLabel: 'Tegund', + hostLabel: 'Hýsill / Nafn', + valueLabel: 'Gildi', }, }, - domainTaken: { - title: 'Þetta lén ({{domain}}) er þegar með SSO-tengingu', - subtitle: 'Hafðu samband við stjórnanda forritsins til að fá aðgang í gegnum núverandi tengingu.', - }, }, }, createOrganization: { diff --git a/packages/localizations/src/it-IT.ts b/packages/localizations/src/it-IT.ts index 619225ef990..64a75cbfc43 100644 --- a/packages/localizations/src/it-IT.ts +++ b/packages/localizations/src/it-IT.ts @@ -202,30 +202,28 @@ export const itIT: LocalizationResource = { }, warning: 'Una volta selezionato un provider non potrai cambiarlo fino al termine della configurazione', }, - verifyEmailDomainStep: { - title: 'Verifica indirizzo email', - subtitle: "Verifica l'indirizzo email su cui vuoi abilitare la connessione aziendale.", - addEmailAddress: { - formTitle: 'Abbiamo bisogno della tua email', - formSubtitle: 'Per iniziare avremo bisogno del tuo indirizzo email', - inputPlaceholder: 'name@company.com', - inputLabel: 'Indirizzo email', - }, - emailCode: { - formTitle: 'Verifica il tuo indirizzo email', - formSubtitle: 'Inserisci il codice di verifica inviato a {{identifier}}', - resendButton: 'Non hai ricevuto il codice? Invia di nuovo', - verified: { - title: 'Abbiamo ricevuto la tua email', - subtitle: 'Hai verificato il tuo indirizzo email con la seguente email', - inputLabel: 'Indirizzo email verificato', + organizationDomainsStep: { + title: 'Aggiungi domini SSO', + subtitle: 'Aggiungi e verifica la proprietà dei domini che la tua organizzazione utilizza per accedere.', + formFieldLabel__domain: 'Domini', + formFieldInputPlaceholder__domain: 'Digita qui il tuo dominio e clicca su Aggiungi per iniziare', + formButtonPrimary__add: 'Aggiungi', + domainSuggestion: { + messageLabel: 'La tua email usa {{domain}}. Vuoi aggiungerlo?', + formButtonPrimary__add: 'Aggiungi {{domain}}', + }, + domainCard: { + badge__verified: 'Verificato', + badge__unverified: 'Non verificato', + verifiedAtLabel: "Verificato il {{ date | shortDate('it-IT') }}", + txtRecord: { + instructions: + 'Aggiungi questo record TXT al tuo provider DNS. Verificheremo automaticamente non appena il record sarà attivo.', + typeLabel: 'Tipo', + hostLabel: 'Host / Nome', + valueLabel: 'Valore', }, }, - domainTaken: { - title: 'Questo dominio ({{domain}}) ha già una connessione SSO', - subtitle: - "Contatta l'amministratore dell'applicazione per ottenere l'accesso tramite la connessione esistente.", - }, }, }, createOrganization: { diff --git a/packages/localizations/src/ja-JP.ts b/packages/localizations/src/ja-JP.ts index 35ecb633605..0383fecb75d 100644 --- a/packages/localizations/src/ja-JP.ts +++ b/packages/localizations/src/ja-JP.ts @@ -207,29 +207,28 @@ export const jaJP: LocalizationResource = { }, warning: 'プロバイダーを選択すると、設定が完了するまで変更できません', }, - verifyEmailDomainStep: { - title: 'メールアドレスを確認', - subtitle: 'エンタープライズ接続を有効にしたいメールアドレスを確認します。', - addEmailAddress: { - formTitle: 'メールアドレスが必要です', - formSubtitle: '開始するにはメールアドレスが必要です', - inputPlaceholder: 'name@company.com', - inputLabel: 'メールアドレス', - }, - emailCode: { - formTitle: 'メールアドレスを確認', - formSubtitle: '{{identifier}} に送信された確認コードを入力してください', - resendButton: 'コードを受け取っていませんか?再送信', - verified: { - title: 'メールを受け取りました', - subtitle: '次のメールでメールアドレスを確認しました', - inputLabel: '確認済みメールアドレス', + organizationDomainsStep: { + title: 'SSO ドメインを追加', + subtitle: '組織がサインインに使用するドメインを追加し、所有権を確認します。', + formFieldLabel__domain: 'ドメイン', + formFieldInputPlaceholder__domain: 'ここにドメインを入力し、「追加」をクリックして開始します', + formButtonPrimary__add: '追加', + domainSuggestion: { + messageLabel: 'あなたのメールは {{domain}} を使用しています。追加しますか?', + formButtonPrimary__add: '{{domain}} を追加', + }, + domainCard: { + badge__verified: '確認済み', + badge__unverified: '未確認', + verifiedAtLabel: "{{ date | shortDate('ja-JP') }} に確認", + txtRecord: { + instructions: + 'この TXT レコードを DNS プロバイダーに追加してください。レコードが有効になると自動的に確認します。', + typeLabel: 'タイプ', + hostLabel: 'ホスト / 名前', + valueLabel: '値', }, }, - domainTaken: { - title: 'このドメイン ({{domain}}) にはすでに SSO 接続が存在します', - subtitle: '既存の接続を通じてアクセスを取得するには、アプリケーションの管理者にお問い合わせください。', - }, }, }, createOrganization: { diff --git a/packages/localizations/src/kk-KZ.ts b/packages/localizations/src/kk-KZ.ts index ce4c0f5a143..44386f28789 100644 --- a/packages/localizations/src/kk-KZ.ts +++ b/packages/localizations/src/kk-KZ.ts @@ -196,29 +196,28 @@ export const kkKZ: LocalizationResource = { }, warning: 'Провайдер таңдалғаннан кейін, конфигурация аяқталғанша өзгерте алмайсыз', }, - verifyEmailDomainStep: { - title: 'Электрондық пошта мекенжайын растау', - subtitle: 'Кәсіпорын байланысын іске қосқыңыз келетін электрондық пошта мекенжайын растаңыз.', - addEmailAddress: { - formTitle: 'Бізге электрондық поштаңыз қажет', - formSubtitle: 'Бастау үшін электрондық пошта мекенжайыңыз қажет болады', - inputPlaceholder: 'name@company.com', - inputLabel: 'Электрондық пошта мекенжайы', - }, - emailCode: { - formTitle: 'Электрондық пошта мекенжайыңызды растаңыз', - formSubtitle: '{{identifier}} мекенжайына жіберілген растау кодын енгізіңіз', - resendButton: 'Код алмадыңыз ба? Қайта жіберу', - verified: { - title: 'Электрондық поштаңызды алдық', - subtitle: 'Электрондық пошта мекенжайыңызды келесі поштамен растадыңыз', - inputLabel: 'Расталған электрондық пошта мекенжайы', + organizationDomainsStep: { + title: 'SSO домендерін қосу', + subtitle: 'Ұйымыңыз кіру үшін пайдаланатын домендердің меншігін қосып, растаңыз.', + formFieldLabel__domain: 'Домендер', + formFieldInputPlaceholder__domain: 'Доменіңізді осы жерге енгізіп, бастау үшін «Қосу» түймесін басыңыз', + formButtonPrimary__add: 'Қосу', + domainSuggestion: { + messageLabel: 'Электрондық поштаңыз {{domain}} пайдаланады. Оны қосқыңыз келе ме?', + formButtonPrimary__add: '{{domain}} қосу', + }, + domainCard: { + badge__verified: 'Расталған', + badge__unverified: 'Расталмаған', + verifiedAtLabel: "{{ date | shortDate('kk-KZ') }} күні расталды", + txtRecord: { + instructions: + 'Бұл TXT жазбасын DNS провайдеріңізге қосыңыз. Жазба белсенді болған бойда біз оны автоматты түрде растаймыз.', + typeLabel: 'Түрі', + hostLabel: 'Хост / Атау', + valueLabel: 'Мән', }, }, - domainTaken: { - title: 'Бұл доменде ({{domain}}) бұрыннан SSO байланысы бар', - subtitle: 'Қолданыстағы байланыс арқылы кіруге қол жеткізу үшін қолданба әкімшісіне хабарласыңыз.', - }, }, }, createOrganization: { diff --git a/packages/localizations/src/ko-KR.ts b/packages/localizations/src/ko-KR.ts index 38e8112a117..ee22e56e44e 100644 --- a/packages/localizations/src/ko-KR.ts +++ b/packages/localizations/src/ko-KR.ts @@ -203,29 +203,27 @@ export const koKR: LocalizationResource = { }, warning: '공급자를 선택하면 구성이 완료될 때까지 다시 변경할 수 없습니다', }, - verifyEmailDomainStep: { - title: '이메일 주소 확인', - subtitle: '엔터프라이즈 연결을 활성화하려는 이메일 주소를 확인하세요.', - addEmailAddress: { - formTitle: '이메일이 필요합니다', - formSubtitle: '시작하려면 이메일 주소가 필요합니다', - inputPlaceholder: 'name@company.com', - inputLabel: '이메일 주소', - }, - emailCode: { - formTitle: '이메일 주소를 확인하세요', - formSubtitle: '{{identifier}}(으)로 전송된 인증 코드를 입력하세요', - resendButton: '코드를 받지 못하셨나요? 다시 보내기', - verified: { - title: '이메일을 받았습니다', - subtitle: '다음 이메일로 이메일 주소를 확인했습니다', - inputLabel: '확인된 이메일 주소', + organizationDomainsStep: { + title: 'SSO 도메인 추가', + subtitle: '조직에서 로그인에 사용하는 도메인의 소유권을 추가하고 확인하세요.', + formFieldLabel__domain: '도메인', + formFieldInputPlaceholder__domain: '여기에 도메인을 입력하고 추가를 클릭하여 시작하세요', + formButtonPrimary__add: '추가', + domainSuggestion: { + messageLabel: '이메일이 {{domain}}을(를) 사용합니다. 추가하시겠습니까?', + formButtonPrimary__add: '{{domain}} 추가', + }, + domainCard: { + badge__verified: '확인됨', + badge__unverified: '미확인', + verifiedAtLabel: "{{ date | shortDate('ko-KR') }}에 확인됨", + txtRecord: { + instructions: '이 TXT 레코드를 DNS 공급자에 추가하세요. 레코드가 활성화되면 자동으로 확인합니다.', + typeLabel: '유형', + hostLabel: '호스트 / 이름', + valueLabel: '값', }, }, - domainTaken: { - title: '이 도메인 ({{domain}}) 에는 이미 SSO 연결이 있습니다', - subtitle: '기존 연결을 통해 접근 권한을 받으려면 애플리케이션 관리자에게 문의하세요.', - }, }, }, createOrganization: { diff --git a/packages/localizations/src/mn-MN.ts b/packages/localizations/src/mn-MN.ts index 4935f8b22aa..fabc4cf42d4 100644 --- a/packages/localizations/src/mn-MN.ts +++ b/packages/localizations/src/mn-MN.ts @@ -196,29 +196,28 @@ export const mnMN: LocalizationResource = { }, warning: 'Үйлчилгээ үзүүлэгчийг сонгосны дараа тохиргоо дуустал өөрчлөх боломжгүй', }, - verifyEmailDomainStep: { - title: 'И-мэйл хаягийг баталгаажуулах', - subtitle: 'Та байгууллагын холболтыг идэвхжүүлэхийг хүсэж буй и-мэйл хаягийг баталгаажуулна уу.', - addEmailAddress: { - formTitle: 'Бид таны и-мэйл хэрэгтэй', - formSubtitle: 'Эхлэхийн тулд бид таны и-мэйл хаягийг авах шаардлагатай', - inputPlaceholder: 'name@company.com', - inputLabel: 'И-мэйл хаяг', - }, - emailCode: { - formTitle: 'И-мэйл хаягаа баталгаажуулна уу', - formSubtitle: '{{identifier}} рүү илгээсэн баталгаажуулах кодыг оруулна уу', - resendButton: 'Код хүлээж аваагүй юу? Дахин илгээх', - verified: { - title: 'Бид таны и-мэйлийг хүлээн авлаа', - subtitle: 'Та доорх и-мэйлээр и-мэйл хаягаа баталгаажуулсан', - inputLabel: 'Баталгаажсан и-мэйл хаяг', + organizationDomainsStep: { + title: 'SSO домэйн нэмэх', + subtitle: 'Танай байгууллагын нэвтрэхэд ашигладаг домэйнуудын эзэмшлийг нэмж баталгаажуулна уу.', + formFieldLabel__domain: 'Домэйн', + formFieldInputPlaceholder__domain: 'Домэйнээ энд бичээд эхлэхийн тулд нэмэх дээр дарна уу', + formButtonPrimary__add: 'Нэмэх', + domainSuggestion: { + messageLabel: 'Таны имэйл {{domain}}-г ашиглаж байна. Үүнийг нэмэх үү?', + formButtonPrimary__add: '{{domain}} нэмэх', + }, + domainCard: { + badge__verified: 'Баталгаажсан', + badge__unverified: 'Баталгаажаагүй', + verifiedAtLabel: "{{ date | shortDate('mn-MN') }}-нд баталгаажсан", + txtRecord: { + instructions: + 'Энэ TXT бичлэгийг өөрийн DNS үйлчилгээ үзүүлэгчид нэмнэ үү. Бичлэг идэвхжсэний дараа бид автоматаар баталгаажуулна.', + typeLabel: 'Төрөл', + hostLabel: 'Хост / Нэр', + valueLabel: 'Утга', }, }, - domainTaken: { - title: 'Энэ домейн ({{domain}}) аль хэдийн SSO холболттой байна', - subtitle: 'Одоо байгаа холболтоор дамжуулан хандах эрх авахын тулд програмын администратортой холбогдоно уу.', - }, }, }, createOrganization: { diff --git a/packages/localizations/src/ms-MY.ts b/packages/localizations/src/ms-MY.ts index 4d2afdf435b..c170a5a2321 100644 --- a/packages/localizations/src/ms-MY.ts +++ b/packages/localizations/src/ms-MY.ts @@ -204,29 +204,28 @@ export const msMY: LocalizationResource = { }, warning: 'Setelah pembekal dipilih anda tidak boleh menukar lagi sehingga konfigurasi selesai', }, - verifyEmailDomainStep: { - title: 'Sahkan alamat e-mel', - subtitle: 'Sahkan alamat e-mel yang anda ingin dayakan sambungan enterprise.', - addEmailAddress: { - formTitle: 'Kami memerlukan e-mel anda', - formSubtitle: 'Untuk memulakan kami memerlukan alamat e-mel anda', - inputPlaceholder: 'name@company.com', - inputLabel: 'Alamat e-mel', - }, - emailCode: { - formTitle: 'Sahkan alamat e-mel anda', - formSubtitle: 'Masukkan kod pengesahan yang dihantar ke {{identifier}}', - resendButton: 'Tidak menerima kod? Hantar semula', - verified: { - title: 'Kami menerima e-mel anda', - subtitle: 'Anda telah mengesahkan alamat e-mel anda dengan e-mel berikut', - inputLabel: 'Alamat e-mel disahkan', + organizationDomainsStep: { + title: 'Tambah domain SSO', + subtitle: 'Tambah dan sahkan pemilikan domain yang digunakan organisasi anda untuk log masuk.', + formFieldLabel__domain: 'Domain', + formFieldInputPlaceholder__domain: 'Taip domain anda di sini dan klik tambah untuk bermula', + formButtonPrimary__add: 'Tambah', + domainSuggestion: { + messageLabel: 'E-mel anda menggunakan {{domain}}. Adakah anda mahu menambahkannya?', + formButtonPrimary__add: 'Tambah {{domain}}', + }, + domainCard: { + badge__verified: 'Disahkan', + badge__unverified: 'Belum disahkan', + verifiedAtLabel: "Disahkan pada {{ date | shortDate('ms-MY') }}", + txtRecord: { + instructions: + 'Tambahkan rekod TXT ini kepada penyedia DNS anda. Kami akan mengesahkan secara automatik sebaik sahaja rekod aktif.', + typeLabel: 'Jenis', + hostLabel: 'Hos / Nama', + valueLabel: 'Nilai', }, }, - domainTaken: { - title: 'Domain ini ({{domain}}) sudah mempunyai sambungan SSO', - subtitle: 'Hubungi pentadbir aplikasi untuk mendapatkan akses melalui sambungan sedia ada.', - }, }, }, createOrganization: { diff --git a/packages/localizations/src/nb-NO.ts b/packages/localizations/src/nb-NO.ts index 0833499c8f4..4db505c7719 100644 --- a/packages/localizations/src/nb-NO.ts +++ b/packages/localizations/src/nb-NO.ts @@ -225,29 +225,28 @@ export const nbNO: LocalizationResource = { }, warning: 'Når en leverandør er valgt, kan du ikke endre igjen før konfigurasjonen er ferdig', }, - verifyEmailDomainStep: { - title: 'Verifiser e-postadresse', - subtitle: 'Verifiser e-postadressen du vil aktivere virksomhetstilkoblingen på.', - addEmailAddress: { - formTitle: 'Vi trenger e-posten din', - formSubtitle: 'For å starte trenger vi e-postadressen din', - inputPlaceholder: 'name@company.com', - inputLabel: 'E-postadresse', - }, - emailCode: { - formTitle: 'Verifiser e-postadressen din', - formSubtitle: 'Skriv inn verifiseringskoden som ble sendt til {{identifier}}', - resendButton: 'Mottok du ingen kode? Send på nytt', - verified: { - title: 'Vi mottok e-posten din', - subtitle: 'Du har verifisert e-postadressen din med følgende e-post', - inputLabel: 'Verifisert e-postadresse', + organizationDomainsStep: { + title: 'Legg til SSO-domener', + subtitle: 'Legg til og bekreft eierskapet til domenene organisasjonen din bruker for å logge på.', + formFieldLabel__domain: 'Domener', + formFieldInputPlaceholder__domain: 'Skriv inn domenet ditt her, og klikk på legg til for å starte', + formButtonPrimary__add: 'Legg til', + domainSuggestion: { + messageLabel: 'E-posten din bruker {{domain}}. Vil du legge den til?', + formButtonPrimary__add: 'Legg til {{domain}}', + }, + domainCard: { + badge__verified: 'Verifisert', + badge__unverified: 'Ikke verifisert', + verifiedAtLabel: "Verifisert den {{ date | shortDate('nb-NO') }}", + txtRecord: { + instructions: + 'Legg til denne TXT-posten hos DNS-leverandøren din. Vi verifiserer automatisk når posten er aktiv.', + typeLabel: 'Type', + hostLabel: 'Vert / Navn', + valueLabel: 'Verdi', }, }, - domainTaken: { - title: 'Dette domenet ({{domain}}) har allerede en SSO-tilkobling', - subtitle: 'Kontakt programmets administrator for å få tilgang via den eksisterende tilkoblingen.', - }, }, }, createOrganization: { diff --git a/packages/localizations/src/nl-BE.ts b/packages/localizations/src/nl-BE.ts index f836c45b447..26b0054ee1c 100644 --- a/packages/localizations/src/nl-BE.ts +++ b/packages/localizations/src/nl-BE.ts @@ -196,30 +196,28 @@ export const nlBE: LocalizationResource = { }, warning: 'Zodra een provider is geselecteerd, kun je deze niet meer wijzigen totdat de configuratie is voltooid', }, - verifyEmailDomainStep: { - title: 'E-mailadres verifiëren', - subtitle: 'Verifieer het e-mailadres waarop u de enterprise-verbinding wilt inschakelen.', - addEmailAddress: { - formTitle: 'We hebben uw e-mail nodig', - formSubtitle: 'Om te beginnen hebben we uw e-mailadres nodig', - inputPlaceholder: 'name@company.com', - inputLabel: 'E-mailadres', - }, - emailCode: { - formTitle: 'Verifieer uw e-mailadres', - formSubtitle: 'Voer de verificatiecode in die is verzonden naar {{identifier}}', - resendButton: 'Geen code ontvangen? Opnieuw verzenden', - verified: { - title: 'We hebben uw e-mail ontvangen', - subtitle: 'U heeft uw e-mailadres geverifieerd met de volgende e-mail', - inputLabel: 'Geverifieerd e-mailadres', + organizationDomainsStep: { + title: 'SSO-domeinen toevoegen', + subtitle: 'Voeg de domeinen toe die je organisatie gebruikt om aan te melden en verifieer het eigendom ervan.', + formFieldLabel__domain: 'Domeinen', + formFieldInputPlaceholder__domain: 'Typ hier uw domein en klik op toevoegen om te beginnen', + formButtonPrimary__add: 'Toevoegen', + domainSuggestion: { + messageLabel: 'Je e-mailadres gebruikt {{domain}}. Wil je het toevoegen?', + formButtonPrimary__add: '{{domain}} toevoegen', + }, + domainCard: { + badge__verified: 'Geverifieerd', + badge__unverified: 'Niet geverifieerd', + verifiedAtLabel: "Geverifieerd op {{ date | shortDate('nl-BE') }}", + txtRecord: { + instructions: + 'Voeg dit TXT-record toe aan je DNS-provider. We verifiëren het automatisch zodra het record actief is.', + typeLabel: 'Type', + hostLabel: 'Host / Naam', + valueLabel: 'Waarde', }, }, - domainTaken: { - title: 'Dit domein ({{domain}}) heeft al een SSO-verbinding', - subtitle: - 'Neem contact op met de beheerder van de applicatie om toegang te krijgen via de bestaande verbinding.', - }, }, }, createOrganization: { diff --git a/packages/localizations/src/nl-NL.ts b/packages/localizations/src/nl-NL.ts index 1ce7af52f1f..cda03f8b7ec 100644 --- a/packages/localizations/src/nl-NL.ts +++ b/packages/localizations/src/nl-NL.ts @@ -196,30 +196,28 @@ export const nlNL: LocalizationResource = { }, warning: 'Zodra een provider is geselecteerd, kun je deze niet meer wijzigen totdat de configuratie is voltooid', }, - verifyEmailDomainStep: { - title: 'E-mailadres verifiëren', - subtitle: 'Verifieer het e-mailadres waarop je de enterprise-verbinding wilt inschakelen.', - addEmailAddress: { - formTitle: 'We hebben je e-mail nodig', - formSubtitle: 'Om te beginnen hebben we je e-mailadres nodig', - inputPlaceholder: 'name@company.com', - inputLabel: 'E-mailadres', - }, - emailCode: { - formTitle: 'Verifieer je e-mailadres', - formSubtitle: 'Voer de verificatiecode in die is verzonden naar {{identifier}}', - resendButton: 'Geen code ontvangen? Opnieuw verzenden', - verified: { - title: 'We hebben je e-mail ontvangen', - subtitle: 'Je hebt je e-mailadres geverifieerd met de volgende e-mail', - inputLabel: 'Geverifieerd e-mailadres', + organizationDomainsStep: { + title: 'SSO-domeinen toevoegen', + subtitle: 'Voeg de domeinen toe die je organisatie gebruikt om in te loggen en verifieer het eigendom ervan.', + formFieldLabel__domain: 'Domeinen', + formFieldInputPlaceholder__domain: 'Typ hier uw domein en klik op toevoegen om te beginnen', + formButtonPrimary__add: 'Toevoegen', + domainSuggestion: { + messageLabel: 'Je e-mailadres gebruikt {{domain}}. Wil je het toevoegen?', + formButtonPrimary__add: '{{domain}} toevoegen', + }, + domainCard: { + badge__verified: 'Geverifieerd', + badge__unverified: 'Niet geverifieerd', + verifiedAtLabel: "Geverifieerd op {{ date | shortDate('nl-NL') }}", + txtRecord: { + instructions: + 'Voeg dit TXT-record toe aan je DNS-provider. We verifiëren het automatisch zodra het record actief is.', + typeLabel: 'Type', + hostLabel: 'Host / Naam', + valueLabel: 'Waarde', }, }, - domainTaken: { - title: 'Dit domein ({{domain}}) heeft al een SSO-verbinding', - subtitle: - 'Neem contact op met de beheerder van de applicatie om toegang te krijgen via de bestaande verbinding.', - }, }, }, createOrganization: { diff --git a/packages/localizations/src/pl-PL.ts b/packages/localizations/src/pl-PL.ts index ad5cad86f3e..0179b01f37f 100644 --- a/packages/localizations/src/pl-PL.ts +++ b/packages/localizations/src/pl-PL.ts @@ -196,29 +196,28 @@ export const plPL: LocalizationResource = { }, warning: 'Po wybraniu dostawcy nie można go ponownie zmienić aż do zakończenia konfiguracji', }, - verifyEmailDomainStep: { - title: 'Zweryfikuj adres e-mail', - subtitle: 'Zweryfikuj adres e-mail, na którym chcesz włączyć połączenie firmowe.', - addEmailAddress: { - formTitle: 'Potrzebujemy Twojego e-maila', - formSubtitle: 'Aby rozpocząć, potrzebujemy Twojego adresu e-mail', - inputPlaceholder: 'name@company.com', - inputLabel: 'Adres e-mail', - }, - emailCode: { - formTitle: 'Zweryfikuj swój adres e-mail', - formSubtitle: 'Wprowadź kod weryfikacyjny wysłany na adres {{identifier}}', - resendButton: 'Nie otrzymałeś kodu? Wyślij ponownie', - verified: { - title: 'Otrzymaliśmy Twój e-mail', - subtitle: 'Zweryfikowałeś swój adres e-mail za pomocą następującego e-maila', - inputLabel: 'Zweryfikowany adres e-mail', + organizationDomainsStep: { + title: 'Dodaj domeny SSO', + subtitle: 'Dodaj i zweryfikuj własność domen, których Twoja organizacja używa do logowania.', + formFieldLabel__domain: 'Domeny', + formFieldInputPlaceholder__domain: 'Wpisz tutaj swoją domenę i kliknij dodaj, aby rozpocząć', + formButtonPrimary__add: 'Dodaj', + domainSuggestion: { + messageLabel: 'Twój e-mail używa {{domain}}. Czy chcesz go dodać?', + formButtonPrimary__add: 'Dodaj {{domain}}', + }, + domainCard: { + badge__verified: 'Zweryfikowano', + badge__unverified: 'Niezweryfikowano', + verifiedAtLabel: "Zweryfikowano {{ date | shortDate('pl-PL') }}", + txtRecord: { + instructions: + 'Dodaj ten rekord TXT u swojego dostawcy DNS. Zweryfikujemy go automatycznie, gdy rekord stanie się aktywny.', + typeLabel: 'Typ', + hostLabel: 'Host / Nazwa', + valueLabel: 'Wartość', }, }, - domainTaken: { - title: 'Ta domena ({{domain}}) ma już połączenie SSO', - subtitle: 'Skontaktuj się z administratorem aplikacji, aby uzyskać dostęp przez istniejące połączenie.', - }, }, }, createOrganization: { diff --git a/packages/localizations/src/pt-BR.ts b/packages/localizations/src/pt-BR.ts index 10fac051b9e..e2c84fd08ce 100644 --- a/packages/localizations/src/pt-BR.ts +++ b/packages/localizations/src/pt-BR.ts @@ -203,29 +203,28 @@ export const ptBR: LocalizationResource = { warning: 'Depois que um provedor for selecionado, você não poderá alterá-lo até que a configuração seja concluída', }, - verifyEmailDomainStep: { - title: 'Verificar endereço de e-mail', - subtitle: 'Verifique o endereço de e-mail no qual deseja habilitar a conexão empresarial.', - addEmailAddress: { - formTitle: 'Precisamos do seu e-mail', - formSubtitle: 'Para começar, precisaremos do seu endereço de e-mail', - inputPlaceholder: 'name@company.com', - inputLabel: 'Endereço de e-mail', - }, - emailCode: { - formTitle: 'Verifique seu endereço de e-mail', - formSubtitle: 'Digite o código de verificação enviado para {{identifier}}', - resendButton: 'Não recebeu um código? Reenviar', - verified: { - title: 'Recebemos seu e-mail', - subtitle: 'Você verificou seu endereço de e-mail com o seguinte e-mail', - inputLabel: 'Endereço de e-mail verificado', + organizationDomainsStep: { + title: 'Adicionar domínios de SSO', + subtitle: 'Adicione e verifique a propriedade dos domínios que sua organização usa para fazer login.', + formFieldLabel__domain: 'Domínios', + formFieldInputPlaceholder__domain: 'Digite seu domínio aqui e clique em Adicionar para começar', + formButtonPrimary__add: 'Adicionar', + domainSuggestion: { + messageLabel: 'Seu e-mail usa {{domain}}. Deseja adicioná-lo?', + formButtonPrimary__add: 'Adicionar {{domain}}', + }, + domainCard: { + badge__verified: 'Verificado', + badge__unverified: 'Não verificado', + verifiedAtLabel: "Verificado em {{ date | shortDate('pt-BR') }}", + txtRecord: { + instructions: + 'Adicione este registro TXT ao seu provedor de DNS. Verificaremos automaticamente assim que o registro estiver ativo.', + typeLabel: 'Tipo', + hostLabel: 'Host / Nome', + valueLabel: 'Valor', }, }, - domainTaken: { - title: 'Este domínio ({{domain}}) já possui uma conexão SSO', - subtitle: 'Entre em contato com o administrador da aplicação para obter acesso por meio da conexão existente.', - }, }, }, createOrganization: { diff --git a/packages/localizations/src/pt-PT.ts b/packages/localizations/src/pt-PT.ts index 959a9b93ef9..b33669824b1 100644 --- a/packages/localizations/src/pt-PT.ts +++ b/packages/localizations/src/pt-PT.ts @@ -204,29 +204,28 @@ export const ptPT: LocalizationResource = { }, warning: 'Depois de um fornecedor ser selecionado não pode ser alterado até que a configuração esteja terminada', }, - verifyEmailDomainStep: { - title: 'Verificar endereço de e-mail', - subtitle: 'Verifique o endereço de e-mail no qual pretende ativar a ligação empresarial.', - addEmailAddress: { - formTitle: 'Precisamos do seu e-mail', - formSubtitle: 'Para começar, precisaremos do seu endereço de e-mail', - inputPlaceholder: 'name@company.com', - inputLabel: 'Endereço de e-mail', - }, - emailCode: { - formTitle: 'Verifique o seu endereço de e-mail', - formSubtitle: 'Introduza o código de verificação enviado para {{identifier}}', - resendButton: 'Não recebeu um código? Reenviar', - verified: { - title: 'Recebemos o seu e-mail', - subtitle: 'Verificou o seu endereço de e-mail com o seguinte e-mail', - inputLabel: 'Endereço de e-mail verificado', + organizationDomainsStep: { + title: 'Adicionar domínios de SSO', + subtitle: 'Adicione e verifique a propriedade dos domínios que a sua organização utiliza para iniciar sessão.', + formFieldLabel__domain: 'Domínios', + formFieldInputPlaceholder__domain: 'Escreva aqui o seu domínio e clique em adicionar para começar', + formButtonPrimary__add: 'Adicionar', + domainSuggestion: { + messageLabel: 'O seu e-mail utiliza {{domain}}. Pretende adicioná-lo?', + formButtonPrimary__add: 'Adicionar {{domain}}', + }, + domainCard: { + badge__verified: 'Verificado', + badge__unverified: 'Não verificado', + verifiedAtLabel: "Verificado em {{ date | shortDate('pt-PT') }}", + txtRecord: { + instructions: + 'Adicione este registo TXT ao seu fornecedor de DNS. Verificaremos automaticamente assim que o registo estiver ativo.', + typeLabel: 'Tipo', + hostLabel: 'Anfitrião / Nome', + valueLabel: 'Valor', }, }, - domainTaken: { - title: 'Este domínio ({{domain}}) já possui uma ligação SSO', - subtitle: 'Contacte o administrador da aplicação para obter acesso através da ligação existente.', - }, }, }, createOrganization: { diff --git a/packages/localizations/src/ro-RO.ts b/packages/localizations/src/ro-RO.ts index 8ed87bb076a..9531035c3f5 100644 --- a/packages/localizations/src/ro-RO.ts +++ b/packages/localizations/src/ro-RO.ts @@ -202,29 +202,29 @@ export const roRO: LocalizationResource = { }, warning: 'Odată ce un furnizor este selectat, nu îl puteți schimba până când configurația nu este finalizată', }, - verifyEmailDomainStep: { - title: 'Verifică adresa de e-mail', - subtitle: 'Verifică adresa de e-mail pe care dorești să activezi conexiunea enterprise.', - addEmailAddress: { - formTitle: 'Avem nevoie de e-mailul tău', - formSubtitle: 'Pentru a începe, vom avea nevoie de adresa ta de e-mail', - inputPlaceholder: 'name@company.com', - inputLabel: 'Adresă de e-mail', - }, - emailCode: { - formTitle: 'Verifică adresa ta de e-mail', - formSubtitle: 'Introdu codul de verificare trimis la {{identifier}}', - resendButton: 'Nu ai primit un cod? Retrimite', - verified: { - title: 'Am primit e-mailul tău', - subtitle: 'Ai verificat adresa ta de e-mail cu următorul e-mail', - inputLabel: 'Adresă de e-mail verificată', + organizationDomainsStep: { + title: 'Adaugă domenii SSO', + subtitle: + 'Adaugă și verifică dreptul de proprietate asupra domeniilor pe care organizația ta le folosește pentru autentificare.', + formFieldLabel__domain: 'Domenii', + formFieldInputPlaceholder__domain: 'Scrie aici domeniul tău și fă clic pe adaugă pentru a începe', + formButtonPrimary__add: 'Adaugă', + domainSuggestion: { + messageLabel: 'E-mailul tău folosește {{domain}}. Vrei să-l adaugi?', + formButtonPrimary__add: 'Adaugă {{domain}}', + }, + domainCard: { + badge__verified: 'Verificat', + badge__unverified: 'Neverificat', + verifiedAtLabel: "Verificat pe {{ date | shortDate('ro-RO') }}", + txtRecord: { + instructions: + 'Adaugă această înregistrare TXT la furnizorul tău DNS. O vom verifica automat de îndată ce înregistrarea devine activă.', + typeLabel: 'Tip', + hostLabel: 'Gazdă / Nume', + valueLabel: 'Valoare', }, }, - domainTaken: { - title: 'Acest domeniu ({{domain}}) are deja o conexiune SSO', - subtitle: 'Contactează administratorul aplicației pentru a obține acces prin conexiunea existentă.', - }, }, }, createOrganization: { diff --git a/packages/localizations/src/ru-RU.ts b/packages/localizations/src/ru-RU.ts index 8988d668748..5db72430e67 100644 --- a/packages/localizations/src/ru-RU.ts +++ b/packages/localizations/src/ru-RU.ts @@ -196,29 +196,28 @@ export const ruRU: LocalizationResource = { }, warning: 'После выбора поставщика вы не сможете изменить его до завершения настройки', }, - verifyEmailDomainStep: { - title: 'Подтвердить адрес электронной почты', - subtitle: 'Подтвердите адрес электронной почты, для которого вы хотите включить корпоративное подключение.', - addEmailAddress: { - formTitle: 'Нам нужна ваша электронная почта', - formSubtitle: 'Чтобы начать, нам потребуется ваш адрес электронной почты', - inputPlaceholder: 'name@company.com', - inputLabel: 'Адрес электронной почты', - }, - emailCode: { - formTitle: 'Подтвердите ваш адрес электронной почты', - formSubtitle: 'Введите код подтверждения, отправленный на {{identifier}}', - resendButton: 'Не получили код? Отправить повторно', - verified: { - title: 'Мы получили вашу электронную почту', - subtitle: 'Вы подтвердили свой адрес электронной почты с помощью следующего письма', - inputLabel: 'Подтверждённый адрес электронной почты', + organizationDomainsStep: { + title: 'Добавить домены SSO', + subtitle: 'Добавьте и подтвердите право собственности на домены, которые ваша организация использует для входа.', + formFieldLabel__domain: 'Домены', + formFieldInputPlaceholder__domain: 'Введите домен здесь и нажмите «Добавить», чтобы начать', + formButtonPrimary__add: 'Добавить', + domainSuggestion: { + messageLabel: 'Ваша электронная почта использует {{domain}}. Хотите добавить его?', + formButtonPrimary__add: 'Добавить {{domain}}', + }, + domainCard: { + badge__verified: 'Подтверждён', + badge__unverified: 'Не подтверждён', + verifiedAtLabel: "Подтверждён {{ date | shortDate('ru-RU') }}", + txtRecord: { + instructions: + 'Добавьте эту TXT-запись у своего DNS-провайдера. Мы проверим её автоматически, как только запись станет активной.', + typeLabel: 'Тип', + hostLabel: 'Хост / Имя', + valueLabel: 'Значение', }, }, - domainTaken: { - title: 'Этот домен ({{domain}}) уже имеет SSO-подключение', - subtitle: 'Свяжитесь с администратором приложения, чтобы получить доступ через существующее подключение.', - }, }, }, createOrganization: { diff --git a/packages/localizations/src/sk-SK.ts b/packages/localizations/src/sk-SK.ts index a8c250279c4..081716dee6e 100644 --- a/packages/localizations/src/sk-SK.ts +++ b/packages/localizations/src/sk-SK.ts @@ -196,30 +196,28 @@ export const skSK: LocalizationResource = { }, warning: 'Po výbere poskytovateľa ho nemôžete zmeniť, kým sa konfigurácia neukončí', }, - verifyEmailDomainStep: { - title: 'Overiť e-mailovú adresu', - subtitle: 'Overte e-mailovú adresu, na ktorej chcete povoliť podnikové pripojenie.', - addEmailAddress: { - formTitle: 'Potrebujeme váš e-mail', - formSubtitle: 'Na začiatok budeme potrebovať vašu e-mailovú adresu', - inputPlaceholder: 'name@company.com', - inputLabel: 'E-mailová adresa', - }, - emailCode: { - formTitle: 'Overte svoju e-mailovú adresu', - formSubtitle: 'Zadajte overovací kód odoslaný na {{identifier}}', - resendButton: 'Nedostali ste kód? Odoslať znova', - verified: { - title: 'Máme váš e-mail', - subtitle: 'Overili ste svoju e-mailovú adresu nasledujúcim e-mailom', - inputLabel: 'Overená e-mailová adresa', + organizationDomainsStep: { + title: 'Pridať SSO domény', + subtitle: 'Pridajte a overte vlastníctvo domén, ktoré vaša organizácia používa na prihlásenie.', + formFieldLabel__domain: 'Domény', + formFieldInputPlaceholder__domain: 'Sem zadajte svoju doménu a kliknutím na pridať začnite', + formButtonPrimary__add: 'Pridať', + domainSuggestion: { + messageLabel: 'Váš e-mail používa {{domain}}. Chcete ho pridať?', + formButtonPrimary__add: 'Pridať {{domain}}', + }, + domainCard: { + badge__verified: 'Overené', + badge__unverified: 'Neoverené', + verifiedAtLabel: "Overené {{ date | shortDate('sk-SK') }}", + txtRecord: { + instructions: + 'Pridajte tento záznam TXT u svojho poskytovateľa DNS. Hneď ako bude záznam aktívny, automaticky ho overíme.', + typeLabel: 'Typ', + hostLabel: 'Hostiteľ / Názov', + valueLabel: 'Hodnota', }, }, - domainTaken: { - title: 'Táto doména ({{domain}}) už má SSO pripojenie', - subtitle: - 'Kontaktujte administrátora aplikácie, aby ste získali prístup prostredníctvom existujúceho pripojenia.', - }, }, }, createOrganization: { diff --git a/packages/localizations/src/sr-RS.ts b/packages/localizations/src/sr-RS.ts index db6dcdb34b4..9467fd068b6 100644 --- a/packages/localizations/src/sr-RS.ts +++ b/packages/localizations/src/sr-RS.ts @@ -196,29 +196,28 @@ export const srRS: LocalizationResource = { }, warning: 'Kada se provajder izabere, ne možete ga ponovo menjati dok se konfiguracija ne završi', }, - verifyEmailDomainStep: { - title: 'Potvrdi adresu e-pošte', - subtitle: 'Potvrdite adresu e-pošte na kojoj želite da omogućite enterprise konekciju.', - addEmailAddress: { - formTitle: 'Potrebna nam je vaša e-pošta', - formSubtitle: 'Da bismo započeli, biće nam potrebna vaša adresa e-pošte', - inputPlaceholder: 'name@company.com', - inputLabel: 'Adresa e-pošte', - }, - emailCode: { - formTitle: 'Potvrdite svoju adresu e-pošte', - formSubtitle: 'Unesite verifikacioni kod poslat na {{identifier}}', - resendButton: 'Niste primili kod? Pošalji ponovo', - verified: { - title: 'Primili smo vašu e-poštu', - subtitle: 'Potvrdili ste svoju adresu e-pošte sledećom e-poštom', - inputLabel: 'Potvrđena adresa e-pošte', + organizationDomainsStep: { + title: 'Dodaj SSO domene', + subtitle: 'Dodajte i potvrdite vlasništvo nad domenima koje vaša organizacija koristi za prijavu.', + formFieldLabel__domain: 'Domeni', + formFieldInputPlaceholder__domain: 'Ovde unesite svoj domen i kliknite na dodaj da biste počeli', + formButtonPrimary__add: 'Dodaj', + domainSuggestion: { + messageLabel: 'Vaša e-pošta koristi {{domain}}. Želite li da ga dodate?', + formButtonPrimary__add: 'Dodaj {{domain}}', + }, + domainCard: { + badge__verified: 'Potvrđeno', + badge__unverified: 'Nepotvrđeno', + verifiedAtLabel: "Potvrđeno {{ date | shortDate('sr-RS') }}", + txtRecord: { + instructions: + 'Dodajte ovaj TXT zapis kod svog DNS provajdera. Automatski ćemo ga potvrditi čim zapis postane aktivan.', + typeLabel: 'Tip', + hostLabel: 'Host / Naziv', + valueLabel: 'Vrednost', }, }, - domainTaken: { - title: 'Ovaj domen ({{domain}}) već ima SSO konekciju', - subtitle: 'Kontaktirajte administratora aplikacije da biste dobili pristup putem postojeće konekcije.', - }, }, }, createOrganization: { diff --git a/packages/localizations/src/sv-SE.ts b/packages/localizations/src/sv-SE.ts index 986421ecbc5..2136dfd51ee 100644 --- a/packages/localizations/src/sv-SE.ts +++ b/packages/localizations/src/sv-SE.ts @@ -196,29 +196,28 @@ export const svSE: LocalizationResource = { }, warning: 'När en leverantör har valts kan du inte ändra igen förrän konfigurationen är klar', }, - verifyEmailDomainStep: { - title: 'Verifiera e-postadress', - subtitle: 'Verifiera e-postadressen som du vill aktivera företagsanslutningen för.', - addEmailAddress: { - formTitle: 'Vi behöver din e-post', - formSubtitle: 'För att börja behöver vi din e-postadress', - inputPlaceholder: 'name@company.com', - inputLabel: 'E-postadress', - }, - emailCode: { - formTitle: 'Verifiera din e-postadress', - formSubtitle: 'Ange verifieringskoden som skickats till {{identifier}}', - resendButton: 'Fick du ingen kod? Skicka igen', - verified: { - title: 'Vi har fått din e-post', - subtitle: 'Du har verifierat din e-postadress med följande e-post', - inputLabel: 'Verifierad e-postadress', + organizationDomainsStep: { + title: 'Lägg till SSO-domäner', + subtitle: 'Lägg till och verifiera ägarskapet för de domäner som din organisation använder för att logga in.', + formFieldLabel__domain: 'Domäner', + formFieldInputPlaceholder__domain: 'Skriv din domän här och klicka på lägg till för att börja', + formButtonPrimary__add: 'Lägg till', + domainSuggestion: { + messageLabel: 'Din e-post använder {{domain}}. Vill du lägga till den?', + formButtonPrimary__add: 'Lägg till {{domain}}', + }, + domainCard: { + badge__verified: 'Verifierad', + badge__unverified: 'Overifierad', + verifiedAtLabel: "Verifierad den {{ date | shortDate('sv-SE') }}", + txtRecord: { + instructions: + 'Lägg till den här TXT-posten hos din DNS-leverantör. Vi verifierar automatiskt när posten är aktiv.', + typeLabel: 'Typ', + hostLabel: 'Värd / Namn', + valueLabel: 'Värde', }, }, - domainTaken: { - title: 'Denna domän ({{domain}}) har redan en SSO-anslutning', - subtitle: 'Kontakta applikationens administratör för att få åtkomst via den befintliga anslutningen.', - }, }, }, createOrganization: { diff --git a/packages/localizations/src/ta-IN.ts b/packages/localizations/src/ta-IN.ts index f4557c462e4..0e22978aefd 100644 --- a/packages/localizations/src/ta-IN.ts +++ b/packages/localizations/src/ta-IN.ts @@ -204,29 +204,28 @@ export const taIN: LocalizationResource = { }, warning: 'வழங்குநரைத் தேர்ந்தெடுத்த பிறகு, கட்டமைப்பு முடியும் வரை மீண்டும் மாற்ற முடியாது', }, - verifyEmailDomainStep: { - title: 'மின்னஞ்சல் முகவரியை சரிபார்க்கவும்', - subtitle: 'நீங்கள் நிறுவன இணைப்பை இயக்க விரும்பும் மின்னஞ்சல் முகவரியை சரிபார்க்கவும்.', - addEmailAddress: { - formTitle: 'எங்களுக்கு உங்கள் மின்னஞ்சல் தேவை', - formSubtitle: 'தொடங்க உங்கள் மின்னஞ்சல் முகவரி தேவைப்படும்', - inputPlaceholder: 'name@company.com', - inputLabel: 'மின்னஞ்சல் முகவரி', - }, - emailCode: { - formTitle: 'உங்கள் மின்னஞ்சல் முகவரியை சரிபார்க்கவும்', - formSubtitle: '{{identifier}} க்கு அனுப்பப்பட்ட சரிபார்ப்பு குறியீட்டை உள்ளிடவும்', - resendButton: 'குறியீடு கிடைக்கவில்லையா? மீண்டும் அனுப்பு', - verified: { - title: 'உங்கள் மின்னஞ்சல் கிடைத்தது', - subtitle: 'பின்வரும் மின்னஞ்சலுடன் உங்கள் மின்னஞ்சல் முகவரியை சரிபார்த்துள்ளீர்கள்', - inputLabel: 'சரிபார்க்கப்பட்ட மின்னஞ்சல் முகவரி', + organizationDomainsStep: { + title: 'SSO டொமைன்களைச் சேர்க்கவும்', + subtitle: 'உங்கள் நிறுவனம் உள்நுழைய பயன்படுத்தும் டொமைன்களின் உரிமையைச் சேர்த்து சரிபார்க்கவும்.', + formFieldLabel__domain: 'டொமைன்கள்', + formFieldInputPlaceholder__domain: 'உங்கள் டொமைனை இங்கே தட்டச்சு செய்து, தொடங்க சேர் என்பதைக் கிளிக் செய்யவும்', + formButtonPrimary__add: 'சேர்', + domainSuggestion: { + messageLabel: 'உங்கள் மின்னஞ்சல் {{domain}} ஐப் பயன்படுத்துகிறது. அதைச் சேர்க்க விரும்புகிறீர்களா?', + formButtonPrimary__add: '{{domain}} ஐச் சேர்', + }, + domainCard: { + badge__verified: 'சரிபார்க்கப்பட்டது', + badge__unverified: 'சரிபார்க்கப்படவில்லை', + verifiedAtLabel: "{{ date | shortDate('ta-IN') }} அன்று சரிபார்க்கப்பட்டது", + txtRecord: { + instructions: + 'இந்த TXT பதிவை உங்கள் DNS வழங்குநரிடம் சேர்க்கவும். பதிவு செயலில் வந்தவுடன் நாங்கள் தானாகவே சரிபார்ப்போம்.', + typeLabel: 'வகை', + hostLabel: 'ஹோஸ்ட் / பெயர்', + valueLabel: 'மதிப்பு', }, }, - domainTaken: { - title: 'இந்த டொமைனுக்கு ({{domain}}) ஏற்கனவே SSO இணைப்பு உள்ளது', - subtitle: 'ஏற்கனவே உள்ள இணைப்பின் வழியாக அணுகலைப் பெற, பயன்பாட்டின் நிர்வாகியைத் தொடர்பு கொள்ளவும்.', - }, }, }, createOrganization: { diff --git a/packages/localizations/src/te-IN.ts b/packages/localizations/src/te-IN.ts index b13243dfab6..dd076243020 100644 --- a/packages/localizations/src/te-IN.ts +++ b/packages/localizations/src/te-IN.ts @@ -203,29 +203,28 @@ export const teIN: LocalizationResource = { }, warning: 'ఒకసారి ప్రొవైడర్ ఎంచుకున్న తర్వాత, కాన్ఫిగరేషన్ ముగిసే వరకు మీరు మళ్లీ మార్చలేరు', }, - verifyEmailDomainStep: { - title: 'ఇమెయిల్ చిరునామా ధృవీకరించండి', - subtitle: 'మీరు ఎంటర్‌ప్రైజ్ కనెక్షన్‌ను ప్రారంభించాలనుకుంటున్న ఇమెయిల్ చిరునామాను ధృవీకరించండి.', - addEmailAddress: { - formTitle: 'మాకు మీ ఇమెయిల్ అవసరం', - formSubtitle: 'ప్రారంభించడానికి మాకు మీ ఇమెయిల్ చిరునామా అవసరం', - inputPlaceholder: 'name@company.com', - inputLabel: 'ఇమెయిల్ చిరునామా', - }, - emailCode: { - formTitle: 'మీ ఇమెయిల్ చిరునామాను ధృవీకరించండి', - formSubtitle: '{{identifier}} కు పంపిన ధృవీకరణ కోడ్‌ను నమోదు చేయండి', - resendButton: 'కోడ్ అందలేదా? మళ్లీ పంపండి', - verified: { - title: 'మాకు మీ ఇమెయిల్ వచ్చింది', - subtitle: 'మీరు కింది ఇమెయిల్‌తో మీ ఇమెయిల్ చిరునామాను ధృవీకరించారు', - inputLabel: 'ధృవీకరించబడిన ఇమెయిల్ చిరునామా', + organizationDomainsStep: { + title: 'SSO డొమైన్‌లను జోడించండి', + subtitle: 'మీ సంస్థ సైన్ ఇన్ చేయడానికి ఉపయోగించే డొమైన్‌ల యాజమాన్యాన్ని జోడించి ధృవీకరించండి.', + formFieldLabel__domain: 'డొమైన్‌లు', + formFieldInputPlaceholder__domain: 'మీ డొమైన్‌ను ఇక్కడ టైప్ చేసి, ప్రారంభించడానికి జోడించు క్లిక్ చేయండి', + formButtonPrimary__add: 'జోడించు', + domainSuggestion: { + messageLabel: 'మీ ఇమెయిల్ {{domain}} ను ఉపయోగిస్తుంది. మీరు దీన్ని జోడించాలనుకుంటున్నారా?', + formButtonPrimary__add: '{{domain}} జోడించు', + }, + domainCard: { + badge__verified: 'ధృవీకరించబడింది', + badge__unverified: 'ధృవీకరించబడలేదు', + verifiedAtLabel: "{{ date | shortDate('te-IN') }} న ధృవీకరించబడింది", + txtRecord: { + instructions: + 'ఈ TXT రికార్డ్‌ను మీ DNS ప్రొవైడర్‌కు జోడించండి. రికార్డ్ సక్రియం అయిన వెంటనే మేము స్వయంచాలకంగా ధృవీకరిస్తాము.', + typeLabel: 'రకం', + hostLabel: 'హోస్ట్ / పేరు', + valueLabel: 'విలువ', }, }, - domainTaken: { - title: 'ఈ డొమైన్‌కి ({{domain}}) ఇప్పటికే SSO కనెక్షన్ ఉంది', - subtitle: 'ఇప్పటికే ఉన్న కనెక్షన్ ద్వారా యాక్సెస్ పొందడానికి అప్లికేషన్ నిర్వాహకుడిని సంప్రదించండి.', - }, }, }, createOrganization: { diff --git a/packages/localizations/src/th-TH.ts b/packages/localizations/src/th-TH.ts index 6a8ca5f679e..f027120caac 100644 --- a/packages/localizations/src/th-TH.ts +++ b/packages/localizations/src/th-TH.ts @@ -200,29 +200,27 @@ export const thTH: LocalizationResource = { }, warning: 'เมื่อเลือกผู้ให้บริการแล้วคุณไม่สามารถเปลี่ยนได้อีกจนกว่าการกำหนดค่าจะเสร็จสิ้น', }, - verifyEmailDomainStep: { - title: 'ยืนยันที่อยู่อีเมล', - subtitle: 'ยืนยันที่อยู่อีเมลที่คุณต้องการเปิดใช้งานการเชื่อมต่อองค์กร', - addEmailAddress: { - formTitle: 'เราต้องการอีเมลของคุณ', - formSubtitle: 'เพื่อเริ่มต้น เราต้องการที่อยู่อีเมลของคุณ', - inputPlaceholder: 'name@company.com', - inputLabel: 'ที่อยู่อีเมล', - }, - emailCode: { - formTitle: 'ยืนยันที่อยู่อีเมลของคุณ', - formSubtitle: 'ป้อนรหัสยืนยันที่ส่งไปยัง {{identifier}}', - resendButton: 'ไม่ได้รับรหัส? ส่งใหม่', - verified: { - title: 'เราได้รับอีเมลของคุณแล้ว', - subtitle: 'คุณได้ยืนยันที่อยู่อีเมลของคุณด้วยอีเมลต่อไปนี้', - inputLabel: 'ที่อยู่อีเมลที่ยืนยันแล้ว', + organizationDomainsStep: { + title: 'เพิ่มโดเมน SSO', + subtitle: 'เพิ่มและยืนยันความเป็นเจ้าของโดเมนที่องค์กรของคุณใช้ในการลงชื่อเข้าใช้', + formFieldLabel__domain: 'โดเมน', + formFieldInputPlaceholder__domain: 'พิมพ์โดเมนของคุณที่นี่แล้วคลิกเพิ่มเพื่อเริ่มต้น', + formButtonPrimary__add: 'เพิ่ม', + domainSuggestion: { + messageLabel: 'อีเมลของคุณใช้ {{domain}} คุณต้องการเพิ่มหรือไม่?', + formButtonPrimary__add: 'เพิ่ม {{domain}}', + }, + domainCard: { + badge__verified: 'ยืนยันแล้ว', + badge__unverified: 'ยังไม่ได้ยืนยัน', + verifiedAtLabel: "ยืนยันเมื่อ {{ date | shortDate('th-TH') }}", + txtRecord: { + instructions: 'เพิ่มระเบียน TXT นี้ไปยังผู้ให้บริการ DNS ของคุณ เราจะยืนยันโดยอัตโนมัติเมื่อระเบียนทำงาน', + typeLabel: 'ประเภท', + hostLabel: 'โฮสต์ / ชื่อ', + valueLabel: 'ค่า', }, }, - domainTaken: { - title: 'โดเมนนี้ ({{domain}}) มีการเชื่อมต่อ SSO อยู่แล้ว', - subtitle: 'ติดต่อผู้ดูแลระบบของแอปพลิเคชันเพื่อขอเข้าถึงผ่านการเชื่อมต่อที่มีอยู่', - }, }, }, createOrganization: { diff --git a/packages/localizations/src/tr-TR.ts b/packages/localizations/src/tr-TR.ts index 939380a2176..04dbfd0e1db 100644 --- a/packages/localizations/src/tr-TR.ts +++ b/packages/localizations/src/tr-TR.ts @@ -196,29 +196,28 @@ export const trTR: LocalizationResource = { }, warning: 'Bir sağlayıcı seçildikten sonra yapılandırma bitene kadar tekrar değiştiremezsiniz', }, - verifyEmailDomainStep: { - title: 'E-posta adresini doğrula', - subtitle: 'Kurumsal bağlantıyı etkinleştirmek istediğiniz e-posta adresini doğrulayın.', - addEmailAddress: { - formTitle: 'E-postanıza ihtiyacımız var', - formSubtitle: 'Başlamak için e-posta adresinize ihtiyacımız olacak', - inputPlaceholder: 'name@company.com', - inputLabel: 'E-posta adresi', - }, - emailCode: { - formTitle: 'E-posta adresinizi doğrulayın', - formSubtitle: '{{identifier}} adresine gönderilen doğrulama kodunu girin', - resendButton: 'Kod almadınız mı? Tekrar gönder', - verified: { - title: 'E-postanızı aldık', - subtitle: 'E-posta adresinizi aşağıdaki e-posta ile doğruladınız', - inputLabel: 'Doğrulanmış e-posta adresi', + organizationDomainsStep: { + title: 'SSO alan adları ekle', + subtitle: 'Kuruluşunuzun oturum açmak için kullandığı alan adlarının sahipliğini ekleyin ve doğrulayın.', + formFieldLabel__domain: 'Alan adları', + formFieldInputPlaceholder__domain: 'Alan adınızı buraya yazın ve başlamak için Ekle düğmesine tıklayın', + formButtonPrimary__add: 'Ekle', + domainSuggestion: { + messageLabel: 'E-postanız {{domain}} kullanıyor. Eklemek ister misiniz?', + formButtonPrimary__add: '{{domain}} ekle', + }, + domainCard: { + badge__verified: 'Doğrulandı', + badge__unverified: 'Doğrulanmadı', + verifiedAtLabel: "{{ date | shortDate('tr-TR') }} tarihinde doğrulandı", + txtRecord: { + instructions: + 'Bu TXT kaydını DNS sağlayıcınıza ekleyin. Kayıt yayına girer girmez otomatik olarak doğrulayacağız.', + typeLabel: 'Tür', + hostLabel: 'Ana bilgisayar / Ad', + valueLabel: 'Değer', }, }, - domainTaken: { - title: 'Bu alan adı ({{domain}}) zaten bir SSO bağlantısına sahip', - subtitle: 'Mevcut bağlantı üzerinden erişim sağlamak için uygulamanın yöneticisiyle iletişime geçin.', - }, }, }, createOrganization: { diff --git a/packages/localizations/src/uk-UA.ts b/packages/localizations/src/uk-UA.ts index 9a036c44183..e981b3644c5 100644 --- a/packages/localizations/src/uk-UA.ts +++ b/packages/localizations/src/uk-UA.ts @@ -196,29 +196,28 @@ export const ukUA: LocalizationResource = { }, warning: 'Після вибору постачальника ви не зможете змінити його, доки не буде завершено налаштування', }, - verifyEmailDomainStep: { - title: 'Підтвердити адресу електронної пошти', - subtitle: "Підтвердьте адресу електронної пошти, на якій ви хочете увімкнути корпоративне з'єднання.", - addEmailAddress: { - formTitle: 'Нам потрібна ваша електронна пошта', - formSubtitle: 'Щоб почати, нам знадобиться ваша адреса електронної пошти', - inputPlaceholder: 'name@company.com', - inputLabel: 'Адреса електронної пошти', - }, - emailCode: { - formTitle: 'Підтвердьте вашу адресу електронної пошти', - formSubtitle: 'Введіть код підтвердження, надісланий на {{identifier}}', - resendButton: 'Не отримали код? Надіслати повторно', - verified: { - title: 'Ми отримали вашу електронну пошту', - subtitle: 'Ви підтвердили адресу електронної пошти за допомогою наступного листа', - inputLabel: 'Підтверджена адреса електронної пошти', + organizationDomainsStep: { + title: 'Додати домени SSO', + subtitle: 'Додайте та підтвердьте право власності на домени, які ваша організація використовує для входу.', + formFieldLabel__domain: 'Домени', + formFieldInputPlaceholder__domain: 'Введіть свій домен тут і натисніть «Додати», щоб почати', + formButtonPrimary__add: 'Додати', + domainSuggestion: { + messageLabel: 'Ваша електронна пошта використовує {{domain}}. Бажаєте додати його?', + formButtonPrimary__add: 'Додати {{domain}}', + }, + domainCard: { + badge__verified: 'Підтверджено', + badge__unverified: 'Не підтверджено', + verifiedAtLabel: "Підтверджено {{ date | shortDate('uk-UA') }}", + txtRecord: { + instructions: + 'Додайте цей TXT-запис до свого DNS-провайдера. Ми перевіримо його автоматично, щойно запис стане активним.', + typeLabel: 'Тип', + hostLabel: 'Хост / Імʼя', + valueLabel: 'Значення', }, }, - domainTaken: { - title: "Цей домен ({{domain}}) уже має з'єднання SSO", - subtitle: "Зверніться до адміністратора програми, щоб отримати доступ через існуюче з'єднання.", - }, }, }, createOrganization: { diff --git a/packages/localizations/src/utils/enUS_v4.ts b/packages/localizations/src/utils/enUS_v4.ts index a2958700f55..39441e435f1 100644 --- a/packages/localizations/src/utils/enUS_v4.ts +++ b/packages/localizations/src/utils/enUS_v4.ts @@ -548,6 +548,7 @@ export const enUS_v4: any = { badge__automaticInvitation: 'Automatic invitations', badge__automaticSuggestion: 'Automatic suggestions', badge__manualInvitation: 'No automatic enrollment', + badge__enterpriseSso: 'Enterprise SSO', start: { headerTitle__members: 'Members', headerTitle__settings: 'Settings', @@ -581,7 +582,7 @@ export const enUS_v4: any = { subtitle: 'Allow users to join the organization automatically or request to join based on a verified email domain.', primaryButton: 'Add domain', - unverifiedDomain_menuAction__verify: 'Verify domain', + unverifiedDomain_menuAction__verify: 'Verify domains', unverifiedDomain_menuAction__remove: 'Delete domain', }, }, diff --git a/packages/localizations/src/vi-VN.ts b/packages/localizations/src/vi-VN.ts index 683236376df..89a1524163f 100644 --- a/packages/localizations/src/vi-VN.ts +++ b/packages/localizations/src/vi-VN.ts @@ -202,29 +202,28 @@ export const viVN: LocalizationResource = { }, warning: 'Khi đã chọn nhà cung cấp, bạn không thể thay đổi cho đến khi cấu hình hoàn tất', }, - verifyEmailDomainStep: { - title: 'Xác minh địa chỉ email', - subtitle: 'Xác minh địa chỉ email mà bạn muốn kích hoạt kết nối doanh nghiệp.', - addEmailAddress: { - formTitle: 'Chúng tôi cần email của bạn', - formSubtitle: 'Để bắt đầu, chúng tôi sẽ cần địa chỉ email của bạn', - inputPlaceholder: 'name@company.com', - inputLabel: 'Địa chỉ email', - }, - emailCode: { - formTitle: 'Xác minh địa chỉ email của bạn', - formSubtitle: 'Nhập mã xác minh đã được gửi tới {{identifier}}', - resendButton: 'Không nhận được mã? Gửi lại', - verified: { - title: 'Chúng tôi đã nhận được email của bạn', - subtitle: 'Bạn đã xác minh địa chỉ email của mình với email sau', - inputLabel: 'Địa chỉ email đã xác minh', + organizationDomainsStep: { + title: 'Thêm tên miền SSO', + subtitle: 'Thêm và xác minh quyền sở hữu các tên miền mà tổ chức của bạn dùng để đăng nhập.', + formFieldLabel__domain: 'Tên miền', + formFieldInputPlaceholder__domain: 'Nhập tên miền của bạn vào đây và nhấp vào thêm để bắt đầu', + formButtonPrimary__add: 'Thêm', + domainSuggestion: { + messageLabel: 'Email của bạn sử dụng {{domain}}. Bạn có muốn thêm nó không?', + formButtonPrimary__add: 'Thêm {{domain}}', + }, + domainCard: { + badge__verified: 'Đã xác minh', + badge__unverified: 'Chưa xác minh', + verifiedAtLabel: "Đã xác minh vào {{ date | shortDate('vi-VN') }}", + txtRecord: { + instructions: + 'Thêm bản ghi TXT này vào nhà cung cấp DNS của bạn. Chúng tôi sẽ tự động xác minh khi bản ghi hoạt động.', + typeLabel: 'Loại', + hostLabel: 'Máy chủ / Tên', + valueLabel: 'Giá trị', }, }, - domainTaken: { - title: 'Tên miền này ({{domain}}) đã có kết nối SSO', - subtitle: 'Liên hệ với quản trị viên của ứng dụng để có quyền truy cập thông qua kết nối hiện có.', - }, }, }, createOrganization: { diff --git a/packages/localizations/src/zh-CN.ts b/packages/localizations/src/zh-CN.ts index ba3034405f5..ff96ea5f707 100644 --- a/packages/localizations/src/zh-CN.ts +++ b/packages/localizations/src/zh-CN.ts @@ -196,29 +196,27 @@ export const zhCN: LocalizationResource = { }, warning: '选择提供商后,在配置完成之前无法再次更改', }, - verifyEmailDomainStep: { - title: '验证电子邮件地址', - subtitle: '验证您想要启用企业连接的电子邮件地址。', - addEmailAddress: { - formTitle: '我们需要您的电子邮件', - formSubtitle: '为了开始,我们需要您的电子邮件地址', - inputPlaceholder: 'name@company.com', - inputLabel: '电子邮件地址', - }, - emailCode: { - formTitle: '验证您的电子邮件地址', - formSubtitle: '输入发送到 {{identifier}} 的验证码', - resendButton: '没有收到验证码?重新发送', - verified: { - title: '我们已收到您的电子邮件', - subtitle: '您已使用以下电子邮件验证了您的电子邮件地址', - inputLabel: '已验证的电子邮件地址', + organizationDomainsStep: { + title: '添加 SSO 域名', + subtitle: '添加并验证贵组织用于登录的域名的所有权。', + formFieldLabel__domain: '域名', + formFieldInputPlaceholder__domain: '在此输入您的域名,然后点击添加即可开始', + formButtonPrimary__add: '添加', + domainSuggestion: { + messageLabel: '您的邮箱使用了 {{domain}}。是否要添加它?', + formButtonPrimary__add: '添加 {{domain}}', + }, + domainCard: { + badge__verified: '已验证', + badge__unverified: '未验证', + verifiedAtLabel: "验证于 {{ date | shortDate('zh-CN') }}", + txtRecord: { + instructions: '将此 TXT 记录添加到您的 DNS 提供商。记录生效后,我们将自动进行验证。', + typeLabel: '类型', + hostLabel: '主机 / 名称', + valueLabel: '值', }, }, - domainTaken: { - title: '此域名 ({{domain}}) 已存在 SSO 连接', - subtitle: '请联系应用程序管理员,通过现有连接获取访问权限。', - }, }, }, createOrganization: { diff --git a/packages/localizations/src/zh-TW.ts b/packages/localizations/src/zh-TW.ts index 3740bc4fc6f..ebe4e768dcf 100644 --- a/packages/localizations/src/zh-TW.ts +++ b/packages/localizations/src/zh-TW.ts @@ -202,29 +202,27 @@ export const zhTW: LocalizationResource = { }, warning: '選擇提供者後,在設定完成之前無法再次變更', }, - verifyEmailDomainStep: { - title: '驗證電子郵件地址', - subtitle: '驗證您想要啟用企業連線的電子郵件地址。', - addEmailAddress: { - formTitle: '我們需要您的電子郵件', - formSubtitle: '為了開始,我們需要您的電子郵件地址', - inputPlaceholder: 'name@company.com', - inputLabel: '電子郵件地址', - }, - emailCode: { - formTitle: '驗證您的電子郵件地址', - formSubtitle: '輸入發送到 {{identifier}} 的驗證碼', - resendButton: '沒有收到驗證碼?重新發送', - verified: { - title: '我們已收到您的電子郵件', - subtitle: '您已使用以下電子郵件驗證了您的電子郵件地址', - inputLabel: '已驗證的電子郵件地址', + organizationDomainsStep: { + title: '新增 SSO 網域', + subtitle: '新增並驗證貴組織用於登入的網域的所有權。', + formFieldLabel__domain: '網域', + formFieldInputPlaceholder__domain: '在此輸入您的網域,然後點選新增即可開始', + formButtonPrimary__add: '新增', + domainSuggestion: { + messageLabel: '您的電子郵件使用了 {{domain}}。是否要新增它?', + formButtonPrimary__add: '新增 {{domain}}', + }, + domainCard: { + badge__verified: '已驗證', + badge__unverified: '未驗證', + verifiedAtLabel: "驗證於 {{ date | shortDate('zh-TW') }}", + txtRecord: { + instructions: '請將此 TXT 記錄新增至您的 DNS 供應商。記錄生效後,我們將自動進行驗證。', + typeLabel: '類型', + hostLabel: '主機 / 名稱', + valueLabel: '值', }, }, - domainTaken: { - title: '此網域 ({{domain}}) 已存在 SSO 連線', - subtitle: '請聯絡應用程式管理員,透過現有連線取得存取權限。', - }, }, }, createOrganization: { diff --git a/packages/shared/src/react/hooks/index.ts b/packages/shared/src/react/hooks/index.ts index e4ab76177bb..34b1f771e1b 100644 --- a/packages/shared/src/react/hooks/index.ts +++ b/packages/shared/src/react/hooks/index.ts @@ -43,6 +43,8 @@ export type { UseOrganizationEnterpriseConnectionsParams, UseOrganizationEnterpriseConnectionsReturn, } from './useOrganizationEnterpriseConnections'; +export { __internal_useOrganizationDomains } from './useOrganizationDomains'; +export type { UseOrganizationDomainsParams, UseOrganizationDomainsReturn } from './useOrganizationDomains'; export { __internal_useOrganizationEnterpriseConnectionTestRuns } from './useOrganizationEnterpriseConnectionTestRuns'; export type { UseOrganizationEnterpriseConnectionTestRunsParams, diff --git a/packages/shared/src/react/hooks/useOrganizationDomains.shared.ts b/packages/shared/src/react/hooks/useOrganizationDomains.shared.ts new file mode 100644 index 00000000000..83992c840b1 --- /dev/null +++ b/packages/shared/src/react/hooks/useOrganizationDomains.shared.ts @@ -0,0 +1,24 @@ +import { useMemo } from 'react'; + +import { INTERNAL_STABLE_KEYS } from '../stable-keys'; +import { createCacheKeys } from './createCacheKeys'; + +/** + * @internal + */ +export function useOrganizationDomainsCacheKeys(params: { organizationId: string | null; enrollmentMode?: string }) { + const { organizationId, enrollmentMode } = params; + return useMemo(() => { + return createCacheKeys({ + stablePrefix: INTERNAL_STABLE_KEYS.ORGANIZATION_DOMAINS_KEY, + authenticated: Boolean(organizationId), + tracked: { + organizationId: organizationId ?? null, + enrollmentMode: enrollmentMode ?? null, + }, + untracked: { + args: {}, + }, + }); + }, [organizationId, enrollmentMode]); +} diff --git a/packages/shared/src/react/hooks/useOrganizationDomains.tsx b/packages/shared/src/react/hooks/useOrganizationDomains.tsx new file mode 100644 index 00000000000..054a9f388a2 --- /dev/null +++ b/packages/shared/src/react/hooks/useOrganizationDomains.tsx @@ -0,0 +1,206 @@ +import { useCallback, useEffect, useMemo } from 'react'; + +import { logger } from '../../logger'; +import type { GetDomainsParams } from '../../types/organization'; +import type { + OrganizationDomainResource, + OrganizationDomainsBulkOwnershipVerificationResource, + OrganizationEnrollmentMode, +} from '../../types/organizationDomain'; +import { useClerkInstanceContext } from '../contexts'; +import { defineKeepPreviousDataFn } from '../query/keep-previous-data'; +import { useClerkQueryClient } from '../query/use-clerk-query-client'; +import { useClerkQuery } from '../query/useQuery'; +import { useOrganizationBase } from './base/useOrganizationBase'; +import { useClearQueriesOnSignOut } from './useClearQueriesOnSignOut'; +import { useOrganizationDomainsCacheKeys } from './useOrganizationDomains.shared'; + +const OWNERSHIP_VERIFICATION_POLL_INTERVAL_MS = 10_000; + +export type UseOrganizationDomainsParams = { + enabled?: boolean; + keepPreviousData?: boolean; + /** + * Filter the returned domains by enrollment mode. + */ + enrollmentMode?: OrganizationEnrollmentMode; +}; + +export type UseOrganizationDomainsReturn = { + data: OrganizationDomainResource[] | undefined; + totalCount: number | undefined; + error: Error | null; + isLoading: boolean; + isFetching: boolean; + /** + * Creates a new domain for the active organization, derived from the given name. + */ + createDomain: (name: string) => Promise; + /** + * Issues a fresh TXT challenge for each of the given domains in a single + * request. Each resolved domain's `ownershipVerification` carries the + * `txtRecordName` and `txtRecordValue`. + */ + prepareOwnershipVerification: ( + domains: OrganizationDomainResource[], + ) => Promise; + /** + * Resolves the published TXT records for the given domains to complete ownership verification. + */ + attemptOwnershipVerification: ( + domains: OrganizationDomainResource[], + ) => Promise; + revalidate: () => Promise; +}; + +/** + * Domains for the active organization. + * + * @internal + */ +function useOrganizationDomains(params: UseOrganizationDomainsParams = {}): UseOrganizationDomainsReturn { + const { keepPreviousData = true, enabled = true, enrollmentMode } = params; + const clerk = useClerkInstanceContext(); + const organization = useOrganizationBase(); + const [queryClient] = useClerkQueryClient(); + + const { queryKey, stableKey, authenticated } = useOrganizationDomainsCacheKeys({ + organizationId: organization?.id ?? null, + enrollmentMode, + }); + + const queryEnabled = enabled && clerk.loaded && Boolean(organization); + + useClearQueriesOnSignOut({ + isSignedOut: organization === null, + authenticated, + stableKeys: stableKey, + }); + + const fetchParams: GetDomainsParams | undefined = enrollmentMode ? { enrollmentMode } : undefined; + + const query = useClerkQuery({ + queryKey, + queryFn: () => organization?.getDomains(fetchParams), + enabled: queryEnabled, + placeholderData: defineKeepPreviousDataFn(keepPreviousData), + }); + + const revalidate = useCallback( + () => queryClient.invalidateQueries({ queryKey: [stableKey] }), + [queryClient, stableKey], + ); + + const createDomain = useCallback( + async (name: string) => { + let created = await organization?.createDomain(name, enrollmentMode ? { enrollmentMode } : undefined); + + if (created && enrollmentMode === 'enterprise_sso') { + const prepared = await organization?.prepareOwnershipVerification([created.id]); + created = prepared?.data[0] ?? created; + } + + await revalidate(); + return created; + }, + [organization, revalidate, enrollmentMode], + ); + + const prepareOwnershipVerification = useCallback( + async (domains: OrganizationDomainResource[]) => { + const prepared = await organization?.prepareOwnershipVerification(domains.map(domain => domain.id)); + await revalidate(); + return prepared; + }, + [organization, revalidate], + ); + + const attemptOwnershipVerification = useCallback( + async (domains: OrganizationDomainResource[]) => { + const attempted = await organization?.attemptOwnershipVerification(domains.map(domain => domain.id)); + await revalidate(); + return attempted; + }, + [organization, revalidate], + ); + + const response = query.data; + + const unverifiedOwnershipDomainIds = useMemo( + () => + (response?.data ?? []) + .filter( + (domain: OrganizationDomainResource) => + domain.ownershipVerification && domain.ownershipVerification.status !== 'verified', + ) + .map((domain: OrganizationDomainResource) => domain.id), + [response?.data], + ); + + const unverifiedOwnershipKey = unverifiedOwnershipDomainIds.join(','); + + // Poll `attempt_ownership_verification` for the outstanding unverified domains + // until none remain. + useEffect(() => { + if (!queryEnabled || !organization || !unverifiedOwnershipKey) { + return; + } + + let cancelled = false; + let timeoutId: ReturnType; + + const scheduleNext = () => { + timeoutId = setTimeout(() => void runAttempt(), OWNERSHIP_VERIFICATION_POLL_INTERVAL_MS); + }; + + const domainIds = unverifiedOwnershipKey.split(','); + + const runAttempt = async () => { + const result = await organization.attemptOwnershipVerification(domainIds).catch((error: unknown) => { + logger.warnOnce(`Clerk: failed to attempt organization domain ownership verification: ${error}`); + return undefined; + }); + if (cancelled) { + return; + } + + // Refetch the domains list after every attempt so the UI reflects the + // latest ownership status + await revalidate(); + if (cancelled) { + return; + } + + // Stop polling once every domain in the attempt response is verified + const allVerified = + !!result?.data.length && result.data.every(domain => domain.ownershipVerification?.status === 'verified'); + if (allVerified) { + return; + } + + scheduleNext(); + }; + + scheduleNext(); + + return () => { + cancelled = true; + clearTimeout(timeoutId); + }; + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [unverifiedOwnershipKey, queryEnabled]); + + return { + data: response?.data, + totalCount: response?.total_count, + error: query.error ?? null, + isLoading: query.isLoading, + isFetching: query.isFetching, + createDomain, + prepareOwnershipVerification, + attemptOwnershipVerification, + revalidate, + }; +} + +export { useOrganizationDomains as __internal_useOrganizationDomains }; diff --git a/packages/shared/src/react/stable-keys.ts b/packages/shared/src/react/stable-keys.ts index 628b3640824..eeab3cd627e 100644 --- a/packages/shared/src/react/stable-keys.ts +++ b/packages/shared/src/react/stable-keys.ts @@ -76,6 +76,7 @@ const USER_ENTERPRISE_CONNECTIONS_KEY = 'userEnterpriseConnections'; const ENTERPRISE_CONNECTION_TEST_RUNS_KEY = 'enterpriseConnectionTestRuns'; const ORGANIZATION_ENTERPRISE_CONNECTIONS_KEY = 'organizationEnterpriseConnections'; const ORGANIZATION_ENTERPRISE_CONNECTION_TEST_RUNS_KEY = 'organizationEnterpriseConnectionTestRuns'; +const ORGANIZATION_DOMAINS_KEY = 'organizationDomains'; export const INTERNAL_STABLE_KEYS = { PAYMENT_ATTEMPT_KEY, @@ -85,6 +86,7 @@ export const INTERNAL_STABLE_KEYS = { ENTERPRISE_CONNECTION_TEST_RUNS_KEY, ORGANIZATION_ENTERPRISE_CONNECTIONS_KEY, ORGANIZATION_ENTERPRISE_CONNECTION_TEST_RUNS_KEY, + ORGANIZATION_DOMAINS_KEY, } as const; export type __internal_ResourceCacheStableKey = (typeof INTERNAL_STABLE_KEYS)[keyof typeof INTERNAL_STABLE_KEYS]; diff --git a/packages/shared/src/types/elementIds.ts b/packages/shared/src/types/elementIds.ts index 336279a9a07..94f41a515fc 100644 --- a/packages/shared/src/types/elementIds.ts +++ b/packages/shared/src/types/elementIds.ts @@ -33,7 +33,8 @@ export type FieldId = | 'idpSsoUrl' | 'acsUrl' | 'spEntityId' - | 'web3WalletName'; + | 'web3WalletName' + | 'domain'; export type ProfileSectionId = | 'profile' | 'username' diff --git a/packages/shared/src/types/json.ts b/packages/shared/src/types/json.ts index dff688c85e1..c98ead46920 100644 --- a/packages/shared/src/types/json.ts +++ b/packages/shared/src/types/json.ts @@ -19,7 +19,11 @@ import type { ClerkAPIErrorJSON } from './errors'; import type { EmailAddressIdentifier, UsernameIdentifier } from './identifiers'; import type { ActClaim } from './jwtv2'; import type { OAuthProvider } from './oauth'; -import type { OrganizationDomainVerificationStatus, OrganizationEnrollmentMode } from './organizationDomain'; +import type { + OrganizationDomainOwnershipVerificationStrategy, + OrganizationDomainVerificationStatus, + OrganizationEnrollmentMode, +} from './organizationDomain'; import type { OrganizationInvitationStatus } from './organizationInvitation'; import type { OrganizationCustomRoleKey, OrganizationPermissionKey } from './organizationMembership'; import type { OrganizationSettingsJSON } from './organizationSettings'; @@ -427,6 +431,27 @@ export interface OrganizationDomainVerificationJSON { strategy: 'email_code'; // only available value for now attempts: number; expires_at: number; + verified_at?: number | null; +} + +export interface OrganizationDomainOwnershipVerificationJSON { + status: OrganizationDomainVerificationStatus; + strategy: OrganizationDomainOwnershipVerificationStrategy; + attempts: number | null; + expire_at: number | null; + verified_at: number | null; + /** + * Present only on ownership verification responses immediately after + * `prepare_ownership_verification`. The fully qualified DNS name the org admin + * must publish the TXT record under. + */ + txt_record_name?: string | null; + /** + * Present only on ownership verification responses immediately after + * `prepare_ownership_verification`. The exact value the org admin must publish + * in the TXT record. + */ + txt_record_value?: string | null; } export interface OrganizationDomainJSON extends ClerkResourceJSON { @@ -435,7 +460,12 @@ export interface OrganizationDomainJSON extends ClerkResourceJSON { name: string; organization_id: string; enrollment_mode: OrganizationEnrollmentMode; + /** + * @deprecated Use `affiliation_verification` instead. + */ verification: OrganizationDomainVerificationJSON | null; + affiliation_verification: OrganizationDomainVerificationJSON | null; + ownership_verification: OrganizationDomainOwnershipVerificationJSON | null; affiliation_email_address: string | null; created_at: number; updated_at: number; @@ -443,6 +473,26 @@ export interface OrganizationDomainJSON extends ClerkResourceJSON { total_pending_suggestions: number; } +/** + * A per-domain failure entry returned by the bulk ownership verification + * endpoints, carrying the `organization_domain_id` and the API error code that + * caused it to be skipped. + */ +export interface OrganizationDomainBulkOwnershipVerificationErrorJSON { + id: string; + code: string; +} + +/** + * Partial-success payload returned by the bulk `prepare`/`attempt` + * ownership verification endpoints. Each requested domain id lands in either + * `data` (with its current ownership state) or `errors`. + */ +export interface OrganizationDomainsBulkOwnershipVerificationJSON { + data: OrganizationDomainJSON[]; + errors: OrganizationDomainBulkOwnershipVerificationErrorJSON[]; +} + export interface RoleJSON extends ClerkResourceJSON { object: 'role'; id: string; diff --git a/packages/shared/src/types/localization.ts b/packages/shared/src/types/localization.ts index 5b4feb90948..88418707f7b 100644 --- a/packages/shared/src/types/localization.ts +++ b/packages/shared/src/types/localization.ts @@ -1038,6 +1038,7 @@ export type __internal_LocalizationResource = { badge__automaticInvitation: LocalizationValue; badge__automaticSuggestion: LocalizationValue; badge__manualInvitation: LocalizationValue; + badge__enterpriseSso: LocalizationValue; start: { headerTitle__members: LocalizationValue; membershipSeatUsageLabel: LocalizationValue<'count' | 'limit'>; @@ -1338,29 +1339,27 @@ export type __internal_LocalizationResource = { }; warning: LocalizationValue; }; - verifyEmailDomainStep: { + organizationDomainsStep: { title: LocalizationValue; subtitle: LocalizationValue; - addEmailAddress: { - formTitle: LocalizationValue; - formSubtitle: LocalizationValue; - inputPlaceholder: LocalizationValue; - inputLabel: LocalizationValue; - }; - emailCode: { - formTitle: LocalizationValue; - formSubtitle: LocalizationValue<'identifier'>; - resendButton: LocalizationValue; - verified: { - title: LocalizationValue; - subtitle: LocalizationValue; - inputLabel: LocalizationValue; + formFieldLabel__domain: LocalizationValue; + formFieldInputPlaceholder__domain: LocalizationValue; + formButtonPrimary__add: LocalizationValue; + domainSuggestion: { + messageLabel: LocalizationValue<'domain'>; + formButtonPrimary__add: LocalizationValue<'domain'>; + }; + domainCard: { + badge__verified: LocalizationValue; + badge__unverified: LocalizationValue; + verifiedAtLabel: LocalizationValue<'date'>; + txtRecord: { + instructions: LocalizationValue; + typeLabel: LocalizationValue; + hostLabel: LocalizationValue; + valueLabel: LocalizationValue; }; }; - domainTaken: { - title: LocalizationValue<'domain'>; - subtitle: LocalizationValue; - }; }; testConfigurationStep: { title: LocalizationValue; diff --git a/packages/shared/src/types/organization.ts b/packages/shared/src/types/organization.ts index e3e78b512b0..2d488167ac0 100644 --- a/packages/shared/src/types/organization.ts +++ b/packages/shared/src/types/organization.ts @@ -10,7 +10,12 @@ import type { EnterpriseConnectionTestRunResource, GetEnterpriseConnectionTestRunsParams, } from './enterpriseConnectionTestRun'; -import type { OrganizationDomainResource, OrganizationEnrollmentMode } from './organizationDomain'; +import type { + CreateOrganizationDomainParams, + OrganizationDomainResource, + OrganizationDomainsBulkOwnershipVerificationResource, + OrganizationEnrollmentMode, +} from './organizationDomain'; import type { OrganizationInvitationResource, OrganizationInvitationStatus } from './organizationInvitation'; import type { OrganizationCustomRoleKey, OrganizationMembershipResource } from './organizationMembership'; import type { OrganizationMembershipRequestResource } from './organizationMembershipRequest'; @@ -173,8 +178,29 @@ export interface OrganizationResource extends ClerkResource, BillingPayerMethods * > [!WARNING] * > You must have [**Verified domains**](https://clerk.com/docs/guides/organizations/add-members/verified-domains) enabled in your app's settings in the Clerk Dashboard. * @param domainName - The name of the domain to create. + * @param params - Optional parameters, including the `enrollmentMode` to assign to the new domain. + */ + createDomain: ( + domainName: string, + params?: Pick, + ) => Promise; + /** + * Issues a fresh TXT challenge for each of the given domains in a single + * request. Each resolved domain's `ownershipVerification` carries the + * `txtRecordName` and `txtRecordValue` the org admin must publish. A single + * bad domain does not fail the batch; it lands in `errors`. + * @returns An [`OrganizationDomainsBulkOwnershipVerificationResource`](https://clerk.com/docs/reference/types/organization-domains-bulk-ownership-verification-resource) object. + * @param domainIds - The unique identifiers of the domains to prepare. + */ + prepareOwnershipVerification: (domainIds: string[]) => Promise; + /** + * Resolves the published TXT record for each of the given domains in a single + * request to complete ownership verification. A single bad domain does not + * fail the batch; it lands in `errors`. + * @returns An [`OrganizationDomainsBulkOwnershipVerificationResource`](https://clerk.com/docs/reference/types/organization-domains-bulk-ownership-verification-resource) object. + * @param domainIds - The unique identifiers of the domains to attempt. */ - createDomain: (domainName: string) => Promise; + attemptOwnershipVerification: (domainIds: string[]) => Promise; /** * Gets a domain for an Organization based on the given domain ID. * @returns An [`OrganizationDomainResource`](https://clerk.com/docs/reference/types/organization-domain-resource) object. diff --git a/packages/shared/src/types/organizationDomain.ts b/packages/shared/src/types/organizationDomain.ts index ca4de2c5f45..2cb469c635c 100644 --- a/packages/shared/src/types/organizationDomain.ts +++ b/packages/shared/src/types/organizationDomain.ts @@ -28,8 +28,54 @@ type OrganizationDomainVerificationStrategy = 'email_code'; /** @inline */ export type OrganizationDomainVerificationStatus = 'unverified' | 'verified'; +/** + * The strategy used to verify ownership of an Organization's domain. + * + * @inline + */ +export type OrganizationDomainOwnershipVerificationStrategy = 'txt' | 'legacy' | 'manual_override'; + +/** + * Holds the ownership verification details of an Organization's domain, including + * the TXT record an admin must publish to prove ownership. + */ +export interface OrganizationDomainOwnershipVerification { + /** + * The current status of the domain ownership verification. + */ + status: OrganizationDomainVerificationStatus; + /** + * The strategy used to verify ownership of the domain. + */ + strategy: OrganizationDomainOwnershipVerificationStrategy; + /** + * The number of verification attempts that have been made, or `null` if none. + */ + attempts: number | null; + /** + * The date and time when the current verification attempt expires, or `null`. + */ + expiresAt: Date | null; + /** + * The date and time when ownership of the domain was verified, or `null` if it has not been verified. + */ + verifiedAt: Date | null; + /** + * The fully qualified DNS name the org admin must publish the TXT record under. Present only immediately after preparing ownership verification. + */ + txtRecordName: string | null; + /** + * The exact value the org admin must publish in the TXT record. Present only immediately after preparing ownership verification. + */ + txtRecordValue: string | null; +} + /** @inline */ -export type OrganizationEnrollmentMode = 'manual_invitation' | 'automatic_invitation' | 'automatic_suggestion'; +export type OrganizationEnrollmentMode = + | 'manual_invitation' + | 'automatic_invitation' + | 'automatic_suggestion' + | 'enterprise_sso'; /** * The `OrganizationDomain` object is the model around an Organization's [Verified Domain](https://clerk.com/docs/guides/organizations/add-members/verified-domains). @@ -61,8 +107,18 @@ export interface OrganizationDomainResource extends ClerkResource { enrollmentMode: OrganizationEnrollmentMode; /** * The verification details for the domain, or `null` if the domain has not been verified. + * + * @deprecated Use `affiliationVerification` instead. */ verification: OrganizationDomainVerification | null; + /** + * The affiliation verification details for the domain, or `null` if the domain has not been verified. + */ + affiliationVerification: OrganizationDomainVerification | null; + /** + * The ownership verification details for the domain, or `null` if ownership has not been verified. + */ + ownershipVerification: OrganizationDomainOwnershipVerification | null; /** * The date and time when the domain was created. */ @@ -136,3 +192,50 @@ export type UpdateEnrollmentModeParams = Pick { const ConfigureSSOContent = ({ contentRef }: { contentRef: React.RefObject }) => { const { - isLoading, + isLoading: isLoadingEnterpriseConnection, enterpriseConnection, organizationEnterpriseConnection, testRuns, - mutations, - primaryEmailAddress, + enterpriseConnectionMutations, } = useOrganizationEnterpriseConnection(); - // Gate loading above the provider so the context never observes a loading state. + const { + isLoading: isLoadingOrganizationDomains, + data: organizationDomains, + createDomain, + prepareOwnershipVerification, + attemptOwnershipVerification, + revalidate, + } = __internal_useOrganizationDomains({ + enrollmentMode: 'enterprise_sso', + }); + + const organizationDomainMutations = React.useMemo( + () => ({ createDomain, prepareOwnershipVerification, attemptOwnershipVerification, revalidate }), + [createDomain, prepareOwnershipVerification, attemptOwnershipVerification, revalidate], + ); + + const isLoading = isLoadingEnterpriseConnection || isLoadingOrganizationDomains; if (isLoading) { return ; } @@ -65,8 +81,9 @@ const ConfigureSSOContent = ({ contentRef }: { contentRef: React.RefObject ); diff --git a/packages/ui/src/components/ConfigureSSO/ConfigureSSOContext.tsx b/packages/ui/src/components/ConfigureSSO/ConfigureSSOContext.tsx index f40526d138a..ed068a46a17 100644 --- a/packages/ui/src/components/ConfigureSSO/ConfigureSSOContext.tsx +++ b/packages/ui/src/components/ConfigureSSO/ConfigureSSOContext.tsx @@ -1,9 +1,24 @@ -import type { EmailAddressResource, EnterpriseConnectionResource } from '@clerk/shared/types'; +import type { + EnterpriseConnectionResource, + OrganizationDomainResource, + OrganizationDomainsBulkOwnershipVerificationResource, +} from '@clerk/shared/types'; import React, { type PropsWithChildren } from 'react'; import type { OrganizationEnterpriseConnection } from './domain/organizationEnterpriseConnection'; import type { EnterpriseConnectionMutations, TestRunsView } from './hooks/useOrganizationEnterpriseConnection'; +export interface OrganizationDomainMutations { + createDomain: (name: string) => Promise; + prepareOwnershipVerification: ( + domains: OrganizationDomainResource[], + ) => Promise; + attemptOwnershipVerification: ( + domains: OrganizationDomainResource[], + ) => Promise; + revalidate: () => Promise; +} + /** * Shared state for the ConfigureSSO wizard, persisted across steps. Everything * is sourced from the umbrella `useOrganizationEnterpriseConnection` hook one @@ -14,19 +29,21 @@ export interface ConfigureSSOData { enterpriseConnection: EnterpriseConnectionResource | undefined; /** Ref to the wizard's scrollable content container. */ contentRef: React.RefObject; - mutations: EnterpriseConnectionMutations; + enterpriseConnectionMutations: EnterpriseConnectionMutations; + organizationDomainMutations: OrganizationDomainMutations; organizationEnterpriseConnection: OrganizationEnterpriseConnection; testRuns: TestRunsView; - primaryEmailAddress: EmailAddressResource | undefined; + organizationDomains: OrganizationDomainResource[] | undefined; } interface ConfigureSSOProviderProps { enterpriseConnection: EnterpriseConnectionResource | undefined; organizationEnterpriseConnection: OrganizationEnterpriseConnection; testRuns: TestRunsView; + organizationDomains: OrganizationDomainResource[] | undefined; contentRef: React.RefObject; - mutations: EnterpriseConnectionMutations; - primaryEmailAddress: EmailAddressResource | undefined; + enterpriseConnectionMutations: EnterpriseConnectionMutations; + organizationDomainMutations: OrganizationDomainMutations; } const ConfigureSSOContext = React.createContext(null); @@ -36,9 +53,10 @@ export const ConfigureSSOProvider = ({ enterpriseConnection, organizationEnterpriseConnection, testRuns, + organizationDomains, contentRef, - mutations, - primaryEmailAddress, + enterpriseConnectionMutations, + organizationDomainMutations, children, }: PropsWithChildren): JSX.Element => { const value = React.useMemo( @@ -47,10 +65,19 @@ export const ConfigureSSOProvider = ({ enterpriseConnection, organizationEnterpriseConnection, testRuns, - mutations, - primaryEmailAddress, + organizationDomains, + enterpriseConnectionMutations, + organizationDomainMutations, }), - [contentRef, enterpriseConnection, mutations, organizationEnterpriseConnection, testRuns, primaryEmailAddress], + [ + contentRef, + enterpriseConnectionMutations, + organizationDomainMutations, + organizationEnterpriseConnection, + testRuns, + organizationDomains, + enterpriseConnection, + ], ); return {children}; diff --git a/packages/ui/src/components/ConfigureSSO/ConfigureSSOWizard.tsx b/packages/ui/src/components/ConfigureSSO/ConfigureSSOWizard.tsx index 231a871e348..a878a39e688 100644 --- a/packages/ui/src/components/ConfigureSSO/ConfigureSSOWizard.tsx +++ b/packages/ui/src/components/ConfigureSSO/ConfigureSSOWizard.tsx @@ -4,26 +4,34 @@ import { CardStateProvider } from '@/elements/contexts'; import { ConfigureSSOProvider } from './ConfigureSSOContext'; import { ConfigureSSOHeader } from './ConfigureSSOHeader'; -import { type WizardStepConfig } from './elements/Wizard'; -import { Wizard } from './elements/Wizard'; -import { ConfigureStep, ConfirmationStep, SelectProviderStep, TestConfigurationStep, VerifyDomainStep } from './steps'; +import { Wizard, type WizardStepConfig } from './elements/Wizard'; +import { + ConfigureStep, + ConfirmationStep, + OrganizationDomainsStep, + SelectProviderStep, + TestConfigurationStep, +} from './steps'; export type ConfigureSSOWizardProps = Omit, 'children'>; /** Pure, data-injected ConfigureSSO flow — hosts own fetching, loading, and permission gating. */ export const ConfigureSSOWizard = (props: ConfigureSSOWizardProps): JSX.Element => { - // Guards read from props, not `useConfigureSSO()` — this component renders the provider, so the hook would throw here. - const { organizationEnterpriseConnection: c } = props; + const { organizationEnterpriseConnection: c, organizationDomains } = props; + + const allDomainsVerified = + !!organizationDomains?.length && + organizationDomains.every(domain => domain.ownershipVerification?.status === 'verified'); const steps = React.useMemo( () => [ { id: 'verify-domain', label: 'Verify domain' }, - { id: 'select-provider', guard: () => c.isPrimaryEmailVerified }, - { id: 'configure', label: 'Configure', guard: () => c.isPrimaryEmailVerified && c.hasConnection }, + { id: 'select-provider', guard: () => allDomainsVerified }, + { id: 'configure', label: 'Configure', guard: () => c.hasConnection }, { id: 'test', label: 'Test', guard: () => c.hasMinimumConfiguration || c.isActive }, { id: 'confirmation', label: 'Confirmation', guard: () => c.hasSuccessfulTestRun || c.isActive }, ], - [c], + [c, allDomainsVerified], ); // Each step owns a `CardStateProvider` so card errors stay scoped to their step and clear when it unmounts. @@ -34,7 +42,7 @@ export const ConfigureSSOWizard = (props: ConfigureSSOWizardProps): JSX.Element - + diff --git a/packages/ui/src/components/ConfigureSSO/ResetConnectionDialog.tsx b/packages/ui/src/components/ConfigureSSO/ResetConnectionDialog.tsx index c35bbf9bb46..eed17c9121c 100644 --- a/packages/ui/src/components/ConfigureSSO/ResetConnectionDialog.tsx +++ b/packages/ui/src/components/ConfigureSSO/ResetConnectionDialog.tsx @@ -46,7 +46,7 @@ export const ResetConnectionDialog = (props: ResetConnectionDialogProps): JSX.El const ResetConnectionDialogContent = withCardStateProvider((props: ResetConnectionDialogProps) => { const { onClose, confirmationValue } = props; const card = useCardState(); - const { enterpriseConnection, mutations } = useConfigureSSO(); + const { enterpriseConnection, enterpriseConnectionMutations } = useConfigureSSO(); const confirmationField = useFormControl('deleteConfirmation', '', { type: 'text', @@ -65,13 +65,7 @@ const ResetConnectionDialogContent = withCardStateProvider((props: ResetConnecti } try { - // Reset is a pure delete — no navigation. Dropping `hasConnection` breaks - // the active step's entry guard, so the wizard self-corrects to the - // furthest-reachable step. The mutation is already reverification-wrapped. - // No `useWizard()` here — that lets this dialog be triggered from ANY - // footer (including the nested SAML configure footers) without binding to - // a nested wizard. - await mutations.deleteConnection(enterpriseConnection.id); + await enterpriseConnectionMutations.deleteConnection(enterpriseConnection.id); onClose(); } catch (err) { handleError(err as Error, [confirmationField], card.setError); diff --git a/packages/ui/src/components/ConfigureSSO/__tests__/ConfigureSSO.navigation.test.tsx b/packages/ui/src/components/ConfigureSSO/__tests__/ConfigureSSO.navigation.test.tsx index 7d73059a897..5c97120aaf5 100644 --- a/packages/ui/src/components/ConfigureSSO/__tests__/ConfigureSSO.navigation.test.tsx +++ b/packages/ui/src/components/ConfigureSSO/__tests__/ConfigureSSO.navigation.test.tsx @@ -5,15 +5,6 @@ import { render, waitFor } from '@/test/utils'; import { ConfigureSSO } from '../ConfigureSSO'; -// Integration coverage for the wizard's navigation contract at the rendered- -// component level — the real `ConfigureSSO` → `useOrganizationEnterpriseConnection` -// → `ConfigureSSOWizard` → `` → `useWizardMachine` → step wiring, driven -// only through the connection data the (auto-mocked) FAPI handles return. The -// machine-level behaviours (defer/resolve, clamp) are unit-tested in -// `useWizardMachine.test.tsx`; these tests prove those behaviours hold when the -// connection state changes through the real query/revalidate path that the steps -// actually use, not just a hand-driven config rerender. - const { createFixtures } = bindCreateFixtures('ConfigureSSO'); /** A connection created on the fresh-start path: present but not yet configured. */ @@ -52,7 +43,35 @@ const withAdminOrgUser = (f: any) => { }); }; +const verifiedDomain = { + id: 'dmn_verified', + name: 'clerk.com', + organizationId: 'Org1', + enrollmentMode: 'enterprise_sso', + ownershipVerification: { status: 'verified', strategy: 'txt' }, +} as any; + +const mockVerifiedDomains = (fixtures: any) => + fixtures.clerk.organization?.getDomains.mockResolvedValue({ data: [verifiedDomain], total_count: 1 } as any); + describe('ConfigureSSO wizard navigation (integration)', () => { + it('lands on select-provider when all organization domains are verified (skips verify-domain)', async () => { + const { wrapper, fixtures } = await createFixtures(withAdminOrgUser); + + fixtures.clerk.organization?.getEnterpriseConnections.mockResolvedValue([]); + fixtures.clerk.organization?.getEnterpriseConnectionTestRuns.mockResolvedValue({ + data: [], + total_count: 0, + } as any); + mockVerifiedDomains(fixtures); + + const { findByText, queryByText } = render(, { wrapper }); + + await findByText(/select your identity provider/i); + // verify-domain was skipped, so its subtitle never renders + expect(queryByText(/add and verify ownership of the domains/i)).not.toBeInTheDocument(); + }); + // Contract rules 2 + 8: goNext defers when the next guard is not yet satisfied // (the create has fired but the connection has not landed), then completes on // the render after the data lands — here select-provider → configure on the @@ -71,6 +90,7 @@ describe('ConfigureSSO wizard navigation (integration)', () => { data: [], total_count: 0, } as any); + mockVerifiedDomains(fixtures); const { findByText, findByRole, getByRole, userEvent, queryByText } = render(, { wrapper }); @@ -105,6 +125,7 @@ describe('ConfigureSSO wizard navigation (integration)', () => { data: [], total_count: 0, } as any); + mockVerifiedDomains(fixtures); const { findByText, getByRole, getByLabelText, findByRole, userEvent, queryByText } = render(, { wrapper, @@ -138,6 +159,7 @@ describe('ConfigureSSO wizard navigation (integration)', () => { data: [], total_count: 0, } as any); + mockVerifiedDomains(fixtures); const { findByText, findByRole, getByRole, userEvent, queryByText } = render(, { wrapper }); @@ -175,6 +197,7 @@ describe('ConfigureSSO wizard navigation (integration)', () => { data: [], total_count: 0, } as any); + mockVerifiedDomains(fixtures); const { findByText, getByRole, getByLabelText, findByRole, userEvent, queryByText } = render(, { wrapper, diff --git a/packages/ui/src/components/ConfigureSSO/__tests__/ConfigureSSO.test.tsx b/packages/ui/src/components/ConfigureSSO/__tests__/ConfigureSSO.test.tsx index 5ef98d8a20d..ff44d1f2dc2 100644 --- a/packages/ui/src/components/ConfigureSSO/__tests__/ConfigureSSO.test.tsx +++ b/packages/ui/src/components/ConfigureSSO/__tests__/ConfigureSSO.test.tsx @@ -7,6 +7,25 @@ import { ConfigureSSO } from '../ConfigureSSO'; const { createFixtures } = bindCreateFixtures('ConfigureSSO'); +const verifiedDomain = { + id: 'dmn_verified', + name: 'clerk.com', + organizationId: 'Org1', + enrollmentMode: 'enterprise_sso', + ownershipVerification: { status: 'verified', strategy: 'txt' }, +} as any; + +const unverifiedDomain = { + id: 'dmn_unverified', + name: 'clerk.com', + organizationId: 'Org1', + enrollmentMode: 'enterprise_sso', + ownershipVerification: { status: 'unverified', strategy: 'txt' }, +} as any; + +const mockOrganizationDomains = (fixtures: any, domains: any[]) => + fixtures.clerk.organization?.getDomains.mockResolvedValue({ data: domains, total_count: domains.length } as any); + describe('ConfigureSSO', () => { describe('within an organization', () => { it('shows a warning if the active organization membership lacks the manage enterprise connections permission', async () => { @@ -41,6 +60,7 @@ describe('ConfigureSSO', () => { }); fixtures.clerk.organization?.getEnterpriseConnections.mockResolvedValue([]); + mockOrganizationDomains(fixtures, [verifiedDomain]); const { findByText, queryByText } = render(, { wrapper }); @@ -63,38 +83,37 @@ describe('ConfigureSSO', () => { const { findByText, queryByText } = render(, { wrapper }); - await findByText(/select your identity provider/i); + // No active organization ⇒ no organization domains to verify, so the wizard + // renders its first step (verify-domain). The point of this test is that the + // wizard renders at all without the manage-permission gate. + await findByText(/add and verify ownership of the domains/i); expect(queryByText(/you do not have permission to manage single sign-on/i)).not.toBeInTheDocument(); }); }); describe('state machine mounts on the right step', () => { - it('mounts on verify-domain when the primary email is unverified and there is no connection', async () => { + it('mounts on select-provider when all organization domains are verified and there is no connection', async () => { const { wrapper, fixtures } = await createFixtures(f => { f.withEnterpriseSso({ selfServeSSO: true }); f.withEmailAddress(); f.withOrganizations(); f.withUser({ - // An unverified primary email fails `select-provider`'s guard - // (`isPrimaryEmailVerified`), so the furthest-reachable step is the - // verify-domain entry step rather than select-provider. - email_addresses: [{ email_address: 'test@clerk.com', verification: { status: 'unverified' } }], + email_addresses: ['test@clerk.com'], organization_memberships: [{ name: 'Org1', permissions: ['org:sys_entconns:manage'] }], }); }); fixtures.clerk.organization?.getEnterpriseConnections.mockResolvedValue([]); + mockOrganizationDomains(fixtures, [verifiedDomain]); - const { findByRole, queryByText } = render(, { wrapper }); + const { findByText } = render(, { wrapper }); - // The verify-domain step header is the only step rendered; select-provider - // is gated out behind the unverified email. - await findByRole('heading', { name: /verify email address/i }); - expect(queryByText(/select your identity provider/i)).not.toBeInTheDocument(); + // verify-domain is fulfilled by the verified domains, so the machine skips + // it and lands on select-provider — the first non-fulfilled enabled step. + await findByText(/select your identity provider/i); }); - // TODO: replace with a TXT domain-verification case (ORGS-1594) - it('mounts on select-provider with a verified email and no connection', async () => { + it('stays on verify-domain when not all organization domains are verified', async () => { const { wrapper, fixtures } = await createFixtures(f => { f.withEnterpriseSso({ selfServeSSO: true }); f.withEmailAddress(); @@ -106,12 +125,12 @@ describe('ConfigureSSO', () => { }); fixtures.clerk.organization?.getEnterpriseConnections.mockResolvedValue([]); + mockOrganizationDomains(fixtures, [unverifiedDomain]); - const { findByText } = render(, { wrapper }); + const { findByText, queryByText } = render(, { wrapper }); - // Verified primary email fulfills verify-domain, so the machine skips it - // and lands on select-provider — the first non-fulfilled enabled step. - await findByText(/select your identity provider/i); + await findByText(/add and verify ownership of the domains/i); + expect(queryByText(/select your identity provider/i)).not.toBeInTheDocument(); }); it('short-circuits to the confirmation step for an active connection', async () => { @@ -143,6 +162,7 @@ describe('ConfigureSSO', () => { }, } as any, ]); + mockOrganizationDomains(fixtures, [verifiedDomain]); fixtures.clerk.organization?.getEnterpriseConnectionTestRuns.mockResolvedValue({ data: [], total_count: 0, @@ -183,6 +203,7 @@ describe('ConfigureSSO', () => { }, } as any, ]); + mockOrganizationDomains(fixtures, [verifiedDomain]); // No successful run yet, so the confirmation guard fails and the // furthest-reachable step is `test`. fixtures.clerk.organization?.getEnterpriseConnectionTestRuns.mockResolvedValue({ @@ -225,6 +246,7 @@ describe('ConfigureSSO', () => { }, } as any, ]); + mockOrganizationDomains(fixtures, [verifiedDomain]); // A successful run satisfies the confirmation guard (`hasSuccessfulTestRun`) // even though the connection is still inactive — the success-filtered probe // returns a row, so the furthest-reachable step clears `test` and lands on diff --git a/packages/ui/src/components/ConfigureSSO/__tests__/ResetConnectionDialog.test.tsx b/packages/ui/src/components/ConfigureSSO/__tests__/ResetConnectionDialog.test.tsx index d16271cd6fe..419ca900c4e 100644 --- a/packages/ui/src/components/ConfigureSSO/__tests__/ResetConnectionDialog.test.tsx +++ b/packages/ui/src/components/ConfigureSSO/__tests__/ResetConnectionDialog.test.tsx @@ -5,12 +5,6 @@ import { bindCreateFixtures } from '@/test/create-fixtures'; import { render, screen, waitFor } from '@/test/utils'; import { CardStateProvider } from '@/ui/elements/contexts'; -// The dialog no longer touches the wizard. On confirm it calls the -// reverification-wrapped `mutations.deleteConnection(id)` directly — a pure -// delete, no navigation — and the wizard self-corrects to the -// furthest-reachable step once the active step's guard breaks. That lets the -// dialog be triggered from ANY footer (including nested SAML configure footers) -// without binding to a nested wizard. const deleteConnection = vi.fn(); const connectionMockState = vi.hoisted(() => ({ @@ -23,7 +17,7 @@ vi.mock('../ConfigureSSOContext', () => ({ contentRef: { current: null }, // The dialog's confirm calls the reverification-wrapped `deleteConnection` // mutation directly. No navigation — the wizard self-corrects. - mutations: { deleteConnection }, + enterpriseConnectionMutations: { deleteConnection }, }), })); diff --git a/packages/ui/src/components/ConfigureSSO/domain/__tests__/organizationEnterpriseConnection.test.ts b/packages/ui/src/components/ConfigureSSO/domain/__tests__/organizationEnterpriseConnection.test.ts index 58ed35d632c..ac962c15b85 100644 --- a/packages/ui/src/components/ConfigureSSO/domain/__tests__/organizationEnterpriseConnection.test.ts +++ b/packages/ui/src/components/ConfigureSSO/domain/__tests__/organizationEnterpriseConnection.test.ts @@ -122,21 +122,6 @@ describe('organizationEnterpriseConnection', () => { }); }); - describe('isPrimaryEmailVerified', () => { - it('undefined email → false', () => { - expect(derive({ primaryEmail: undefined }).isPrimaryEmailVerified).toBe(false); - }); - it('null email → false', () => { - expect(derive({ primaryEmail: null }).isPrimaryEmailVerified).toBe(false); - }); - it('unverified email → false', () => { - expect(derive({ primaryEmail: makeEmail('unverified') }).isPrimaryEmailVerified).toBe(false); - }); - it('verified email → true', () => { - expect(derive({ primaryEmail: makeEmail('verified') }).isPrimaryEmailVerified).toBe(true); - }); - }); - describe('hasSuccessfulTestRun', () => { it('false input → false', () => { expect(derive({ hasSuccessfulTestRun: false }).hasSuccessfulTestRun).toBe(false); @@ -214,8 +199,7 @@ describe('organizationEnterpriseConnection', () => { it('is pure: identical inputs produce a deep-equal entity', () => { const connection = makeConnection({ samlConnection: fullyConfiguredSaml, active: true }); - const primaryEmail = makeEmail('verified'); - const inputs = { connection, primaryEmail, hasSuccessfulTestRun: true } as const; + const inputs = { connection, hasSuccessfulTestRun: true } as const; expect(organizationEnterpriseConnection(inputs)).toEqual(organizationEnterpriseConnection(inputs)); }); @@ -232,7 +216,6 @@ describe('organizationEnterpriseConnection', () => { hasConnection: true, isActive: true, hasMinimumConfiguration: true, - isPrimaryEmailVerified: true, hasSuccessfulTestRun: true, status: 'active', }); diff --git a/packages/ui/src/components/ConfigureSSO/domain/organizationEnterpriseConnection.ts b/packages/ui/src/components/ConfigureSSO/domain/organizationEnterpriseConnection.ts index ffcde3c63db..227dbe9dd57 100644 --- a/packages/ui/src/components/ConfigureSSO/domain/organizationEnterpriseConnection.ts +++ b/packages/ui/src/components/ConfigureSSO/domain/organizationEnterpriseConnection.ts @@ -23,7 +23,6 @@ export const connectionBackingEmail = (user: UserResource | null | undefined): E export interface OrganizationEnterpriseConnectionInput { /** FAPI currently supports a single connection per organization. */ connection: EnterpriseConnectionResource | null | undefined; - primaryEmail: EmailAddressResource | null | undefined; /** Probed upstream — not a property of the connection resource itself. */ hasSuccessfulTestRun: boolean; } @@ -40,7 +39,6 @@ export interface OrganizationEnterpriseConnection { readonly hasConnection: boolean; readonly isActive: boolean; readonly hasMinimumConfiguration: boolean; - readonly isPrimaryEmailVerified: boolean; readonly hasSuccessfulTestRun: boolean; readonly status: OrganizationEnterpriseConnectionStatus; } @@ -73,7 +71,6 @@ const connectionStatus = ({ export const organizationEnterpriseConnection = ({ connection, - primaryEmail, hasSuccessfulTestRun, }: OrganizationEnterpriseConnectionInput): OrganizationEnterpriseConnection => { const hasConnection = Boolean(connection); @@ -85,7 +82,6 @@ export const organizationEnterpriseConnection = ({ hasConnection, isActive, hasMinimumConfiguration, - isPrimaryEmailVerified: primaryEmail?.verification?.status === 'verified', hasSuccessfulTestRun, status: connectionStatus({ hasConnection, isActive, hasMinimumConfiguration, hasSuccessfulTestRun }), }; diff --git a/packages/ui/src/components/ConfigureSSO/hooks/__tests__/useOrganizationEnterpriseConnection.test.tsx b/packages/ui/src/components/ConfigureSSO/hooks/__tests__/useOrganizationEnterpriseConnection.test.tsx index b01422fefaf..b56fae83d0b 100644 --- a/packages/ui/src/components/ConfigureSSO/hooks/__tests__/useOrganizationEnterpriseConnection.test.tsx +++ b/packages/ui/src/components/ConfigureSSO/hooks/__tests__/useOrganizationEnterpriseConnection.test.tsx @@ -1,4 +1,3 @@ -import type { EmailAddressResource } from '@clerk/shared/types'; import { renderHook } from '@testing-library/react'; import { beforeEach, describe, expect, it, vi } from 'vitest'; @@ -171,32 +170,39 @@ describe('useOrganizationEnterpriseConnection — test-runs gating', () => { }); describe('useOrganizationEnterpriseConnection — mutations', () => { - const emailAddress = (address: string) => ({ emailAddress: address }) as EmailAddressResource; - - it('createConnection derives name + domains from the email domain and forwards them (no organizationId in body)', async () => { + it('createConnection forwards the provider, the email-derived name, and the given verified domains (no organizationId in body)', async () => { const { result } = renderHook(() => useOrganizationEnterpriseConnection()); - await result.current.mutations.createConnection('saml_okta', emailAddress('admin@acme.com')); + await result.current.enterpriseConnectionMutations.createConnection('saml_okta', ['acme.com', 'example.com']); expect(mutationSpies.create).toHaveBeenCalledTimes(1); + // `name` is derived from the active user's primary email domain + // (admin@clerk.com → clerk.com); `domains` are the verified organization + // domains passed straight through by the caller. expect(mutationSpies.create).toHaveBeenCalledWith({ provider: 'saml_okta', - name: 'acme.com', - domains: ['acme.com'], + name: 'clerk.com', + domains: ['acme.com', 'example.com'], }); }); - it('createConnection resolves to undefined without creating when no email is available', async () => { + it('createConnection forwards an omitted domains list unchanged', async () => { const { result } = renderHook(() => useOrganizationEnterpriseConnection()); - await expect(result.current.mutations.createConnection('saml_okta', undefined)).resolves.toBeUndefined(); - expect(mutationSpies.create).not.toHaveBeenCalled(); + await result.current.enterpriseConnectionMutations.createConnection('saml_okta'); + + expect(mutationSpies.create).toHaveBeenCalledTimes(1); + expect(mutationSpies.create).toHaveBeenCalledWith({ + provider: 'saml_okta', + name: 'clerk.com', + domains: undefined, + }); }); it('setConnectionActive forwards only the active flag to update', async () => { const { result } = renderHook(() => useOrganizationEnterpriseConnection()); - await result.current.mutations.setConnectionActive('ent_1', true); + await result.current.enterpriseConnectionMutations.setConnectionActive('ent_1', true); expect(mutationSpies.update).toHaveBeenCalledWith('ent_1', { active: true }); }); diff --git a/packages/ui/src/components/ConfigureSSO/hooks/useOrganizationEnterpriseConnection.ts b/packages/ui/src/components/ConfigureSSO/hooks/useOrganizationEnterpriseConnection.ts index 12d20b33be8..b22459992fd 100644 --- a/packages/ui/src/components/ConfigureSSO/hooks/useOrganizationEnterpriseConnection.ts +++ b/packages/ui/src/components/ConfigureSSO/hooks/useOrganizationEnterpriseConnection.ts @@ -6,7 +6,6 @@ import { } from '@clerk/shared/react'; import type { DeletedObjectResource, - EmailAddressResource, EnterpriseConnectionResource, EnterpriseConnectionTestRunInitResource, EnterpriseConnectionTestRunResource, @@ -18,10 +17,9 @@ import type { import { useMemo, useRef } from 'react'; import { - connectionBackingEmail, + organizationEnterpriseConnection as buildOrganizationEnterpriseConnection, isEnterpriseConnectionConfigured, type OrganizationEnterpriseConnection, - organizationEnterpriseConnection as buildOrganizationEnterpriseConnection, } from '../domain/organizationEnterpriseConnection'; import type { ProviderType } from '../types'; import { type RefreshTestRunsOptions, useEnterpriseConnectionTestRuns } from './useEnterpriseConnectionTestRuns'; @@ -40,13 +38,9 @@ import { type RefreshTestRunsOptions, useEnterpriseConnectionTestRuns } from './ */ export interface EnterpriseConnectionMutations { /** - * Derives the connection name from the email domain; resolves to `undefined` - * without creating when no primary email is available. + * Creates a new enterprise connection for the active organization. */ - createConnection: ( - provider: ProviderType, - primaryEmailAddress?: EmailAddressResource, - ) => Promise; + createConnection: (provider: ProviderType, domains?: string[]) => Promise; updateConnection: ( id: string, params: UpdateOrganizationEnterpriseConnectionParams, @@ -70,11 +64,9 @@ export interface UseOrganizationEnterpriseConnectionResult { organization: OrganizationResource | null | undefined; /** FAPI currently supports a single connection per organization. */ enterpriseConnection: EnterpriseConnectionResource | undefined; - /** Used to derive the connection name on create. */ - primaryEmailAddress: EmailAddressResource | undefined; /** The domain entity the wizard makes every flow decision from. */ organizationEnterpriseConnection: OrganizationEnterpriseConnection; - mutations: EnterpriseConnectionMutations; + enterpriseConnectionMutations: EnterpriseConnectionMutations; testRuns: TestRunsView; } @@ -164,26 +156,19 @@ export const useOrganizationEnterpriseConnection = (): UseOrganizationEnterprise const { session } = useSession(); const { organization } = useOrganization(); - const primaryEmailAddress = user?.primaryEmailAddress ?? undefined; + const enterpriseConnectionMutations = useMemo(() => { + const createConnection: EnterpriseConnectionMutations['createConnection'] = (provider, domains) => { + const primaryEmailAddress = user?.primaryEmailAddress; + const emailDomain = primaryEmailAddress?.emailAddress.split('@')[1]; - // The connection-domain mutations, defined inline here so the umbrella hook - // owns the single mutation surface. The org-scoped FAPI endpoints have no - // reverification middleware, so these call the underlying handles directly. - const mutations = useMemo(() => { - const createConnection: EnterpriseConnectionMutations['createConnection'] = (provider, primaryEmail) => { - const emailDomain = primaryEmail?.emailAddress.split('@')[1]; + // Connection name will always be defined due to the organization name + // Soon this logic will be moved to the Frontend API + const connectionName = emailDomain ?? organization?.name ?? ''; - if (!emailDomain) { - return Promise.resolve(undefined); - } - - // The organization is inferred from the URL path on the org-scoped - // endpoint, so we don't pass `organizationId` in the body. `domains` is - // required by the create endpoint and is derived from the email domain. return createEnterpriseConnection({ provider, - name: emailDomain, - domains: [emailDomain], + name: connectionName, + domains, }); }; @@ -239,20 +224,15 @@ export const useOrganizationEnterpriseConnection = (): UseOrganizationEnterprise ], ); - // The email whose domain backs the connection — the single domain rule, shared - // with the verify-domain step so the guards and the UI never disagree. - const primaryEmail = connectionBackingEmail(user); - // The single domain entity everything downstream reads decisions from, keyed // on the raw inputs so it is only rebuilt when one of them changes. const organizationEnterpriseConnection = useMemo( () => buildOrganizationEnterpriseConnection({ connection: enterpriseConnection, - primaryEmail, hasSuccessfulTestRun, }), - [enterpriseConnection, primaryEmail, hasSuccessfulTestRun], + [enterpriseConnection, hasSuccessfulTestRun], ); return { @@ -266,9 +246,8 @@ export const useOrganizationEnterpriseConnection = (): UseOrganizationEnterprise // skeleton. isLoading: isLoadingEnterpriseConnections || (hadInitialConnection && isLoadingTestRuns), enterpriseConnection, - primaryEmailAddress, organizationEnterpriseConnection, - mutations, + enterpriseConnectionMutations, testRuns, }; }; diff --git a/packages/ui/src/components/ConfigureSSO/steps/ConfigureStep/saml/SamlCustomConfigureSteps.tsx b/packages/ui/src/components/ConfigureSSO/steps/ConfigureStep/saml/SamlCustomConfigureSteps.tsx index 0d697f7f50e..db88083bb80 100644 --- a/packages/ui/src/components/ConfigureSSO/steps/ConfigureStep/saml/SamlCustomConfigureSteps.tsx +++ b/packages/ui/src/components/ConfigureSSO/steps/ConfigureStep/saml/SamlCustomConfigureSteps.tsx @@ -330,7 +330,7 @@ const SamlCustomIdentityProviderMetadataStep = (): JSX.Element => { const { goNext, goPrev, isFirstStep } = useWizard(); const { enterpriseConnection, - mutations: { updateConnection }, + enterpriseConnectionMutations: { updateConnection }, } = useConfigureSSO(); const samlConnection = enterpriseConnection?.samlConnection; diff --git a/packages/ui/src/components/ConfigureSSO/steps/ConfigureStep/saml/SamlGoogleConfigureSteps.tsx b/packages/ui/src/components/ConfigureSSO/steps/ConfigureStep/saml/SamlGoogleConfigureSteps.tsx index 28fb692f5a3..3fa2c619763 100644 --- a/packages/ui/src/components/ConfigureSSO/steps/ConfigureStep/saml/SamlGoogleConfigureSteps.tsx +++ b/packages/ui/src/components/ConfigureSSO/steps/ConfigureStep/saml/SamlGoogleConfigureSteps.tsx @@ -159,7 +159,7 @@ const SamlGoogleIdentityProviderMetadataStep = (): JSX.Element => { const { goNext, goPrev, isFirstStep } = useWizard(); const { enterpriseConnection, - mutations: { updateConnection }, + enterpriseConnectionMutations: { updateConnection }, } = useConfigureSSO(); const samlConnection = enterpriseConnection?.samlConnection; diff --git a/packages/ui/src/components/ConfigureSSO/steps/ConfigureStep/saml/SamlMicrosoftConfigureSteps.tsx b/packages/ui/src/components/ConfigureSSO/steps/ConfigureStep/saml/SamlMicrosoftConfigureSteps.tsx index 4c273a0c4c1..1fa9a3e491f 100644 --- a/packages/ui/src/components/ConfigureSSO/steps/ConfigureStep/saml/SamlMicrosoftConfigureSteps.tsx +++ b/packages/ui/src/components/ConfigureSSO/steps/ConfigureStep/saml/SamlMicrosoftConfigureSteps.tsx @@ -628,7 +628,7 @@ const SamlMicrosoftIdentityProviderMetadataStep = (): JSX.Element => { const { goNext, goPrev, isFirstStep } = useWizard(); const { enterpriseConnection, - mutations: { updateConnection }, + enterpriseConnectionMutations: { updateConnection }, } = useConfigureSSO(); const samlConnection = enterpriseConnection?.samlConnection; diff --git a/packages/ui/src/components/ConfigureSSO/steps/ConfigureStep/saml/SamlOktaConfigureSteps.tsx b/packages/ui/src/components/ConfigureSSO/steps/ConfigureStep/saml/SamlOktaConfigureSteps.tsx index 5d9b3b307fa..b1f1d4d8996 100644 --- a/packages/ui/src/components/ConfigureSSO/steps/ConfigureStep/saml/SamlOktaConfigureSteps.tsx +++ b/packages/ui/src/components/ConfigureSSO/steps/ConfigureStep/saml/SamlOktaConfigureSteps.tsx @@ -517,7 +517,7 @@ const SamlOktaIdentityProviderMetadataStep = (): JSX.Element => { const { goNext, goPrev, isFirstStep } = useWizard(); const { enterpriseConnection, - mutations: { updateConnection }, + enterpriseConnectionMutations: { updateConnection }, } = useConfigureSSO(); const samlConnection = enterpriseConnection?.samlConnection; diff --git a/packages/ui/src/components/ConfigureSSO/steps/ConfigureStep/saml/__tests__/SamlConfigureSteps.test.tsx b/packages/ui/src/components/ConfigureSSO/steps/ConfigureStep/saml/__tests__/SamlConfigureSteps.test.tsx index a15f2e8d35e..44b8a909db0 100644 --- a/packages/ui/src/components/ConfigureSSO/steps/ConfigureStep/saml/__tests__/SamlConfigureSteps.test.tsx +++ b/packages/ui/src/components/ConfigureSSO/steps/ConfigureStep/saml/__tests__/SamlConfigureSteps.test.tsx @@ -14,7 +14,7 @@ vi.mock('../../../../ConfigureSSOContext', () => ({ enterpriseConnection: undefined, organizationEnterpriseConnection: { hasConnection: false }, provider: 'saml_google', - mutations: {}, + enterpriseConnectionMutations: {}, }), })); diff --git a/packages/ui/src/components/ConfigureSSO/steps/ConfirmationStep.tsx b/packages/ui/src/components/ConfigureSSO/steps/ConfirmationStep.tsx index 6e1a29d3fea..59987b8ab51 100644 --- a/packages/ui/src/components/ConfigureSSO/steps/ConfirmationStep.tsx +++ b/packages/ui/src/components/ConfigureSSO/steps/ConfirmationStep.tsx @@ -76,7 +76,7 @@ export const ConfirmationStep = (): JSX.Element => { const EnableSsoSection = (): JSX.Element => { const { enterpriseConnection, - mutations: { setConnectionActive }, + enterpriseConnectionMutations: { setConnectionActive }, } = useConfigureSSO(); const card = useCardState(); diff --git a/packages/ui/src/components/ConfigureSSO/steps/OrganizationDomainsStep.tsx b/packages/ui/src/components/ConfigureSSO/steps/OrganizationDomainsStep.tsx new file mode 100644 index 00000000000..0488f401b43 --- /dev/null +++ b/packages/ui/src/components/ConfigureSSO/steps/OrganizationDomainsStep.tsx @@ -0,0 +1,494 @@ +import { useUser } from '@clerk/shared/react'; +import type { OrganizationDomainResource } from '@clerk/shared/types'; +import type React from 'react'; +import { useState } from 'react'; + +import { + Badge, + Box, + Button, + Col, + descriptors, + Flex, + Flow, + Icon, + localizationKeys, + Spinner, + Text, + useLocalizations, +} from '@/customizables'; +import { Alert } from '@/elements/Alert'; +import { Animated } from '@/elements/Animated'; +import { ClipboardInput } from '@/elements/ClipboardInput'; +import { useCardState } from '@/elements/contexts'; +import { Field } from '@/elements/FieldControl'; +import { Form } from '@/elements/Form'; +import { Checkmark, Clipboard, Close } from '@/icons'; +import { common } from '@/styledSystem'; +import { useFormControl } from '@/ui/utils/useFormControl'; +import { getFieldError, getGlobalError } from '@/utils/errorHandler'; + +import { useConfigureSSO } from '../ConfigureSSOContext'; +import { Step } from '../elements/Step'; +import { useWizard } from '../elements/Wizard/WizardContext'; + +export const OrganizationDomainsStep = (): JSX.Element => { + const { t } = useLocalizations(); + const { + organizationDomains, + organizationDomainMutations: { createDomain, revalidate }, + } = useConfigureSSO(); + const { goPrev, goNext, isFirstStep, isLastStep } = useWizard(); + const card = useCardState(); + + const handleCreateDomain = async (domain: string) => { + card.setError(undefined); + + try { + await createDomain(domain); + } catch (err: any) { + const apiError = getFieldError(err) ?? getGlobalError(err); + card.setError(apiError); + } + }; + + const hasAllDomainsVerified = + organizationDomains?.length && + organizationDomains?.every(domain => domain.ownershipVerification?.status === 'verified'); + + return ( + + + + + + ({ gap: t.space.$5, minHeight: 0 })} + > + ({ gap: t.space.$4 })}> + + + {!organizationDomains?.length && } + + {card.error && ( + + )} + + + {!!organizationDomains?.length && ( + ({ + gap: t.space.$3, + flex: '0 1 auto', + minHeight: 0, + overflowY: 'auto', + // Inset so card shadows/focus rings are not clipped by the + // scroll container's overflow. + marginInline: `calc(${t.space.$1} * -1)`, + paddingInline: t.space.$1, + ...common.unstyledScrollbar(t), + })} + > + {organizationDomains.map(domain => ( + { + // TODO ORGS-1623 - Add dialog for domain deletion confirmation + void domain.delete().then(() => revalidate()); + }} + /> + ))} + + )} + + + + + goPrev()} + isDisabled={isFirstStep} + /> + goNext()} + isDisabled={isLastStep || !hasAllDomainsVerified} + /> + + + + ); +}; + +/** + * Matches a bare domain such as `example.com` or `sub.example.co.uk`. + * Each label must start and end with an alphanumeric character and a valid + * TLD of at least two letters is required. Protocols, paths, ports, spaces + * and single-label hostnames are rejected. + */ +const DOMAIN_REGEX = /^(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)+[a-z]{2,}$/i; +const isValidDomain = (value: string): boolean => DOMAIN_REGEX.test(value); + +const DomainsField = ({ + onSubmit, + organizationDomains, +}: { + onSubmit: (domain: string) => Promise; + organizationDomains: OrganizationDomainResource[] | undefined; +}): JSX.Element => { + const [isSubmitting, setIsSubmitting] = useState(false); + const domainField = useFormControl('domain', '', { + type: 'text', + label: localizationKeys('configureSSO.organizationDomainsStep.formFieldLabel__domain'), + placeholder: localizationKeys('configureSSO.organizationDomainsStep.formFieldInputPlaceholder__domain'), + }); + + const domain = domainField.value.trim().toLowerCase(); + const canSubmit = isValidDomain(domain) && !organizationDomains?.some(d => d.name === domain) && !isSubmitting; + + const handleSubmit = (e: React.FormEvent) => { + e.preventDefault(); + + if (!canSubmit) { + return; + } + + setIsSubmitting(true); + void onSubmit(domain) + .then(() => domainField.setValue('')) + .finally(() => setIsSubmitting(false)); + }; + + return ( + + + ({ gap: t.space.$2 })} + > + + + + + ({ gap: t.space.$2 })} + > + + + + + + + ); +}; + +const DomainCard = ({ + domain, + onRemove, +}: { + domain: OrganizationDomainResource; + onRemove: () => void; +}): JSX.Element => { + const ownershipVerification = domain.ownershipVerification; + const isVerified = ownershipVerification?.status === 'verified'; + const cardId = isVerified ? 'verified' : 'unverified'; + + return ( + ({ + borderWidth: t.borderWidths.$normal, + borderStyle: t.borderStyles.$solid, + borderColor: t.colors.$borderAlpha150, + borderRadius: t.radii.$lg, + background: t.colors.$colorBackground, + })} + > + ({ gap: t.space.$2, padding: t.space.$4, paddingBottom: t.space.$2 })} + > + ({ gap: t.space.$2, minWidth: 0 })} + > + ({ + fontWeight: t.fontWeights.$medium, + whiteSpace: 'nowrap', + overflow: 'hidden', + textOverflow: 'ellipsis', + })} + > + {domain.name} + + + + + {!isVerified && ( + ({ flexShrink: 0, marginInlineStart: t.space.$1 })} + /> + )} + + + + + + + {ownershipVerification?.verifiedAt ? ( + ({ padding: t.space.$4, paddingTop: 0 })} + /> + ) : ( + + )} + + + ); +}; + +const TxtRecord = ({ + ownershipVerification, +}: { + ownershipVerification: OrganizationDomainResource['ownershipVerification']; +}): JSX.Element => { + return ( + ({ gap: t.space.$3, paddingInline: t.space.$4, paddingBottom: t.space.$4 })} + > + ({ fontSize: t.fontSizes.$sm })} + /> + + ({ + borderTopWidth: t.borderWidths.$normal, + borderTopStyle: t.borderStyles.$solid, + borderTopColor: t.colors.$borderAlpha100, + marginInline: `calc(${t.space.$4} * -1)`, + })} + /> + + ({ gap: t.space.$6 })} + > + + + + + ({ gap: t.space.$2, minWidth: 0 })} + > + ({ fontSize: t.fontSizes.$sm, flexShrink: 0 })} + /> + + + + ); +}; + +const RecordEntry = ({ label, value }: { label: ReturnType; value: string }): JSX.Element => { + return ( + ({ gap: t.space.$2, minWidth: 0 })} + > + ({ fontSize: t.fontSizes.$sm, flexShrink: 0 })} + /> + ({ + fontFamily: t.fonts.$buttons, + display: 'flex', + alignItems: 'center', + justifyContent: 'center', + paddingBlock: t.space.$1, + paddingInline: t.space.$1x5, + })} + > + + {value} + + + + ); +}; diff --git a/packages/ui/src/components/ConfigureSSO/steps/SelectProviderStep.tsx b/packages/ui/src/components/ConfigureSSO/steps/SelectProviderStep.tsx index b9316321e15..6deed20900a 100644 --- a/packages/ui/src/components/ConfigureSSO/steps/SelectProviderStep.tsx +++ b/packages/ui/src/components/ConfigureSSO/steps/SelectProviderStep.tsx @@ -24,13 +24,7 @@ import { Step } from '../elements/Step'; import { useWizard } from '../elements/Wizard'; import type { ProviderType } from '../types'; -/** - * Provider icons whose SVGs are monochromatic and should flip with the - * theme. Mirrors the SUPPORTS_MASK_IMAGE list in `common/ProviderIcon.tsx` - * — keep in sync if either grows. - */ const MONOCHROMATIC_PROVIDER_ICONS: ReadonlySet = new Set(['okta']); - const PROVIDER_GROUPS: ReadonlyArray<{ id: 'saml'; label: LocalizationKey; @@ -62,43 +56,29 @@ const PROVIDER_GROUPS: ReadonlyArray<{ export const SelectProviderStep = (): JSX.Element => { const { - primaryEmailAddress, organizationEnterpriseConnection: c, - mutations: { createConnection }, + enterpriseConnectionMutations: { createConnection }, + organizationDomains, } = useConfigureSSO(); - const { goNext } = useWizard(); + const { goNext, goPrev, isFirstStep } = useWizard(); - // Re-hydrate from context so users returning from another step don't have to - // re-click their provider. const [selected, setSelected] = React.useState(c.provider ?? null); const card = useCardState(); - // Step-LOCAL submit state for the Continue button. `goNext` now DEFERS the - // advance until the next step's guard catches up to the just-resolved create - // (the wizard machine resolves it on the next render), so the shared card - // loading would otherwise leak into the next step as an idle flash. Keeping - // the loading local — and NOT resetting it on success — holds the button in - // its loading state straight through the transition; the deferred advance - // unmounts this step, which ends the loading visually with no idle frame. const [isSubmitting, setIsSubmitting] = React.useState(false); const handleSelect = (next: ProviderType) => { setSelected(next); }; - // On a fresh start the create is unconditional, then `goNext` lands `configure` - // because the resolved create flips `hasConnection`, satisfying its guard. On - // revisit a connection already exists, so we never re-create — `goNext` alone. const handleContinue = async (): Promise => { if (!selected) { return; } - // Connection already created on a prior visit: don't re-create, just - // advance — `configure`'s guard already holds, so this advances immediately - // (no submit, no loading). if (c.hasConnection) { - goNext(); + // TODO ORGS-1607 - Support changing the provider + void goNext(); return; } @@ -106,13 +86,11 @@ export const SelectProviderStep = (): JSX.Element => { setIsSubmitting(true); try { - await createConnection(selected, primaryEmailAddress); - // `goNext` defers; the button STAYS loading and this step unmounts when - // the deferred advance lands. Do NOT reset `isSubmitting` on success. - goNext(); + const domains = organizationDomains?.map(domain => domain.name) ?? []; + await createConnection(selected, domains); + void goNext(); } catch (err) { handleError(err as Error, [], card.setError); - // Re-enable the button ONLY on error — there is no advance to unmount it. setIsSubmitting(false); } }; @@ -186,7 +164,10 @@ export const SelectProviderStep = (): JSX.Element => { - + goPrev()} + isDisabled={isFirstStep} + /> ; - preExistingEmailIdRef: React.MutableRefObject; -}; - -const VerifyDomainRefsContext = createContext(null); - -const useVerifyDomainRefs = (): VerifyDomainRefs => { - const ctx = useContext(VerifyDomainRefsContext); - if (!ctx) { - throw new Error('useVerifyDomainRefs called outside of VerifyDomainStep'); - } - return ctx; -}; - -const ProvideEmailBody = (): JSX.Element => { - const { emailAddressRef, preExistingEmailIdRef } = useVerifyDomainRefs(); - return ( - - - - ); -}; - -const EnterVerificationCodeBody = (): JSX.Element | null => { - const { emailAddressRef } = useVerifyDomainRefs(); - return ( - - - - ); -}; - -// Body-less inner-step graph; the bodies are rendered declaratively via -// `` below. Each body wraps its content in `Step.Body` and reads -// its per-instance refs from context. -const INNER_STEPS: WizardStepConfig[] = [{ id: 'provide-email' }, { id: 'verify-email-address' }]; - -export const VerifyDomainStep = (): JSX.Element => { - const { user } = useUser(); - const { t } = useLocalizations(); - // The nested provide-email → verify-email bubbles its final `goNext` - // up to this top-level wizard; the already-verified branch advances it - // directly via `completeStep`. - const { goNext: completeStep } = useWizard(); - - const emailToVerify = connectionBackingEmail(user); - const isVerified = emailToVerify?.verification.status === 'verified'; - - const wasVerifiedOnMountRef = useRef(isVerified); - const emailAddressRef = useRef(emailToVerify); - const preExistingEmailIdRef = useRef(emailToVerify?.id); - const initialInnerStepIdRef = useRef<'provide-email' | 'verify-email-address'>( - emailToVerify ? 'verify-email-address' : 'provide-email', - ); - - // Memoized so the provider value identity is stable; declared before the - // early returns to keep the hook order unconditional. - const refs = React.useMemo( - () => ({ emailAddressRef, preExistingEmailIdRef }), - [emailAddressRef, preExistingEmailIdRef], - ); - - if (wasVerifiedOnMountRef.current && emailToVerify?.emailAddress) { - return ( - - - - - - - - - - - - - - - - ); - } - - return ( - - - - - - - - - - - - - - - - - - ); -}; - -const isEmail = (str: string) => /^\S+@\S+\.\S+$/.test(str); - -type ProvideEmailStepProps = { - emailAddressRef: React.MutableRefObject; - preExistingEmailIdRef: React.MutableRefObject; -}; - -const normalizeEmail = (value: string): string => value.trim().toLowerCase(); - -export const ProvideEmailStep = ({ emailAddressRef, preExistingEmailIdRef }: ProvideEmailStepProps): JSX.Element => { - const { goNext, goPrev } = useWizard(); - const { user } = useUser(); - const card = useCardState(); - const { t } = useLocalizations(); - // Pre-fill from the parent's tracked email so navigating back from the verify - // step shows what was previously submitted instead of an empty field. - const [email, setEmail] = useState(() => emailAddressRef.current?.emailAddress ?? ''); - const createEmailAddress = useReverification((value: string) => user?.createEmailAddress({ email: value })); - - const canSubmit = isEmail(email) && !card.isLoading; - const handleSubmit = useCallback(async () => { - if (!canSubmit) { - return; - } - - const current = emailAddressRef.current; - const submittedEmail = email.trim(); - - // Same email address as previously submitted, skip the flow - if (current && normalizeEmail(current.emailAddress) === normalizeEmail(submittedEmail)) { - goNext(); - return; - } - - card.setError(undefined); - card.setLoading(); - - try { - const created = await createEmailAddress(submittedEmail); - const previous = current; - emailAddressRef.current = created ?? undefined; - - // Clean up the previous in-flight address so the user doesn't accumulate orphans on - // their account - if (previous && previous.id !== preExistingEmailIdRef.current && previous.id !== created?.id) { - try { - await previous.destroy(); - } catch { - // A leftover unverified address is preferable to surfacing a cleanup - // error after a successful create. - } - } - - goNext(); - } catch (err) { - handleError(err as Error, [], card.setError); - } finally { - card.setIdle(); - } - }, [canSubmit, email, createEmailAddress, card, goNext, emailAddressRef, preExistingEmailIdRef]); - - return ( - <> - - ({ - display: 'flex', - maxWidth: t.sizes.$66, - textAlign: 'center', - flexDirection: 'column', - alignItems: 'center', - })} - > - ({ - width: t.sizes.$8, - height: t.sizes.$8, - color: t.colors.$neutralAlpha600, - })} - /> - - ({ fontSize: t.fontSizes.$lg, fontWeight: t.fontWeights.$bold })} - localizationKey={localizationKeys('configureSSO.verifyEmailDomainStep.addEmailAddress.formTitle')} - /> - - - - - setEmail(e.currentTarget.value)} - hasError={Boolean(card.error)} - isDisabled={card.isLoading} - // eslint-disable-next-line jsx-a11y/no-autofocus - autoFocus - /> - {card.error ? ( - ({ color: t.colors.$danger500, fontSize: t.fontSizes.$sm, textAlign: 'start' })} - > - {card.error} - - ) : null} - - - - - - goPrev()} - isDisabled - /> - - - - ); -}; - -export const EnterVerificationCodeStep = ({ - emailAddressRef, -}: { - emailAddressRef: React.MutableRefObject; -}): JSX.Element | null => { - const { user } = useUser(); - const card = useCardState(); - const { goNext, goPrev } = useWizard(); - const primaryEmailAddress = user?.primaryEmailAddress; - - const emailToVerify = emailAddressRef.current; - const isVerified = emailToVerify?.verification.status === 'verified'; - const isPrimary = emailToVerify?.id === user?.primaryEmailAddressId; - - const prepareEmailVerification = useReverification(() => - emailAddressRef.current?.prepareVerification({ strategy: 'email_code' }), - ); - const attemptEmailVerification = useReverification((code: string) => - emailAddressRef.current?.attemptVerification({ code }), - ); - const setPrimaryEmailAddress = useReverification((emailAddressId: string) => - user?.update({ primaryEmailAddressId: emailAddressId }), - ); - - const prepare = useCallback( - () => prepareEmailVerification()?.catch(err => handleError(err, [], card.setError)), - [prepareEmailVerification, card], - ); - - const otp = useFieldOTP({ - onCodeEntryFinished: (code, resolve, reject) => { - attemptEmailVerification(code) - .then(() => resolve()) - .catch(reject); - }, - onResendCodeClicked: () => { - void prepare(); - }, - onResolve: async () => { - const target = emailAddressRef.current; - if (target && !isPrimary) { - try { - await setPrimaryEmailAddress(target.id); - } catch (err) { - handleError(err as Error, [], card.setError); - return; - } - } - - void goNext(); - }, - }); - - // Send a code on mount, but only when the target address is not already verified - useEffect(() => { - if (emailToVerify && !isVerified) { - void prepare(); - } - // eslint-disable-next-line react-hooks/exhaustive-deps - }, []); - - if (!emailToVerify) { - return null; - } - - return ( - <> - - - - ({ fontSize: t.fontSizes.$sm })} - localizationKey={localizationKeys('configureSSO.verifyEmailDomainStep.emailCode.formTitle')} - /> - - - - - - - - goPrev()} - isDisabled={!!primaryEmailAddress} - /> - goNext()} - isLoading={otp.isLoading || card.isLoading} - isDisabled={!isVerified} - /> - - - ); -}; - -const EmailAlreadyVerified = ({ emailAddress }: { emailAddress: string }): JSX.Element => { - const { t } = useLocalizations(); - - return ( - - ({ - width: t.sizes.$8, - height: t.sizes.$8, - color: t.colors.$neutralAlpha600, - })} - /> - ({ textAlign: 'center', maxWidth: t.sizes.$66 })} - > - ({ fontSize: t.fontSizes.$lg, fontWeight: t.fontWeights.$bold })} - localizationKey={localizationKeys('configureSSO.verifyEmailDomainStep.emailCode.verified.title')} - /> - - - - ({ backgroundColor: t.colors.$neutralAlpha50 })} - /> - - - ); -}; diff --git a/packages/ui/src/components/ConfigureSSO/steps/__tests__/SelectProviderStep.test.tsx b/packages/ui/src/components/ConfigureSSO/steps/__tests__/SelectProviderStep.test.tsx index f1b02c4766b..6243471f3ff 100644 --- a/packages/ui/src/components/ConfigureSSO/steps/__tests__/SelectProviderStep.test.tsx +++ b/packages/ui/src/components/ConfigureSSO/steps/__tests__/SelectProviderStep.test.tsx @@ -10,37 +10,34 @@ import { CardStateProvider } from '@/ui/elements/contexts'; // `handleContinue` that calls the create mutation then `useWizard().goNext`. // We assert on those nav calls. const goNext = vi.fn(); +const goPrev = vi.fn(); vi.mock('../../elements/Wizard', () => ({ - useWizard: () => ({ current: 'select-provider', goNext }), + useWizard: () => ({ current: 'select-provider', goNext, goPrev, isFirstStep: true }), })); const createEnterpriseConnection = vi.fn(); +const VERIFIED_DOMAINS = [{ name: 'clerk.com' }, { name: 'example.com' }]; +const VERIFIED_DOMAIN_NAMES = VERIFIED_DOMAINS.map(domain => domain.name); + // Provider is sourced from the connection entity // (organizationEnterpriseConnection.provider) rather than a context-level // setProvider. The step uses goNext (not goToStep) after a successful create. const contextState = vi.hoisted(() => ({ provider: undefined as 'saml_okta' | 'saml_custom' | undefined, - primaryEmailAddress: { emailAddress: 'test@clerk.com' } as { emailAddress: string } | undefined, - isPrimaryEmailVerified: true, + organizationDomains: [{ name: 'clerk.com' }, { name: 'example.com' }] as Array<{ name: string }> | undefined, })); vi.mock('../../ConfigureSSOContext', () => ({ useConfigureSSO: () => ({ enterpriseConnection: undefined, - // The step's local `handleContinue` reads the reverification-wrapped create - // mutation off the bundled `mutations` object. - mutations: { + enterpriseConnectionMutations: { createConnection: createEnterpriseConnection, }, - primaryEmailAddress: contextState.primaryEmailAddress, - // Provider is sourced from the connection entity; the step no longer reads a - // context-level setProvider — verify-domain runs first, so the create is - // unconditional. + organizationDomains: contextState.organizationDomains, organizationEnterpriseConnection: { provider: contextState.provider, - isPrimaryEmailVerified: contextState.isPrimaryEmailVerified, }, }), })); @@ -58,11 +55,11 @@ const renderStep = ( const resetMocks = () => { goNext.mockReset(); + goPrev.mockReset(); createEnterpriseConnection.mockReset(); createEnterpriseConnection.mockResolvedValue(undefined); contextState.provider = undefined; - contextState.primaryEmailAddress = { emailAddress: 'test@clerk.com' }; - contextState.isPrimaryEmailVerified = true; + contextState.organizationDomains = [{ name: 'clerk.com' }, { name: 'example.com' }]; }; describe('SelectProviderStep', () => { @@ -166,7 +163,7 @@ describe('SelectProviderStep', () => { expect(goNext).toHaveBeenCalled(); }); - expect(createEnterpriseConnection).toHaveBeenCalledWith('saml_okta', contextState.primaryEmailAddress); + expect(createEnterpriseConnection).toHaveBeenCalledWith('saml_okta', VERIFIED_DOMAIN_NAMES); // The create then the goNext are the tail of the call order. expect(callOrder.slice(-2)).toEqual(['createEnterpriseConnection', 'goNext']); }); @@ -183,7 +180,7 @@ describe('SelectProviderStep', () => { expect(goNext).toHaveBeenCalled(); }); - expect(createEnterpriseConnection).toHaveBeenCalledWith('saml_custom', contextState.primaryEmailAddress); + expect(createEnterpriseConnection).toHaveBeenCalledWith('saml_custom', VERIFIED_DOMAIN_NAMES); }); it('does not advance when failing to create enterprise connection', async () => { @@ -201,7 +198,7 @@ describe('SelectProviderStep', () => { await userEvent.click(screen.getByRole('button', { name: /Continue/i })); await waitFor(() => { - expect(createEnterpriseConnection).toHaveBeenCalledWith('saml_okta', contextState.primaryEmailAddress); + expect(createEnterpriseConnection).toHaveBeenCalledWith('saml_okta', VERIFIED_DOMAIN_NAMES); }); expect(goNext).not.toHaveBeenCalled(); @@ -215,12 +212,8 @@ describe('SelectProviderStep', () => { expect(screen.getByRole('button', { name: /Previous/i })).toBeDisabled(); }); - it('always creates and jumps to configure (verify-domain ran first, so no branch back)', async () => { - // Under the new step order the user reaches select-provider only after - // verify-domain, so the create is unconditional even if the verified fact is - // somehow false — there is no longer a branch back to verify-domain. + it('forwards the verified organization domain names to createConnection', async () => { resetMocks(); - contextState.isPrimaryEmailVerified = false; const { wrapper } = await createFixtures(); const { userEvent } = renderStep(wrapper); @@ -232,6 +225,6 @@ describe('SelectProviderStep', () => { expect(goNext).toHaveBeenCalled(); }); - expect(createEnterpriseConnection).toHaveBeenCalledWith('saml_okta', contextState.primaryEmailAddress); + expect(createEnterpriseConnection).toHaveBeenCalledWith('saml_okta', VERIFIED_DOMAIN_NAMES); }); }); diff --git a/packages/ui/src/components/ConfigureSSO/steps/__tests__/TestConfigurationStep.test.tsx b/packages/ui/src/components/ConfigureSSO/steps/__tests__/TestConfigurationStep.test.tsx index c95fab503f2..5c2d2510b44 100644 --- a/packages/ui/src/components/ConfigureSSO/steps/__tests__/TestConfigurationStep.test.tsx +++ b/packages/ui/src/components/ConfigureSSO/steps/__tests__/TestConfigurationStep.test.tsx @@ -47,7 +47,7 @@ vi.mock('../../ConfigureSSOContext', () => ({ }, }, testRuns: testRunsSource, - mutations: { createTestRun }, + enterpriseConnectionMutations: { createTestRun }, }), })); diff --git a/packages/ui/src/components/ConfigureSSO/steps/index.ts b/packages/ui/src/components/ConfigureSSO/steps/index.ts index f505cad9979..49cae50a535 100644 --- a/packages/ui/src/components/ConfigureSSO/steps/index.ts +++ b/packages/ui/src/components/ConfigureSSO/steps/index.ts @@ -1,5 +1,5 @@ export { ConfigureStep } from './ConfigureStep'; export { ConfirmationStep } from './ConfirmationStep'; +export { OrganizationDomainsStep } from './OrganizationDomainsStep'; export { SelectProviderStep } from './SelectProviderStep'; export { TestConfigurationStep } from './TestConfigurationStep'; -export { VerifyDomainStep } from './VerifyDomainStep'; diff --git a/packages/ui/src/components/OrganizationProfile/EnrollmentBadge.tsx b/packages/ui/src/components/OrganizationProfile/EnrollmentBadge.tsx index 0b71ba2f1e1..c60ec2fe026 100644 --- a/packages/ui/src/components/OrganizationProfile/EnrollmentBadge.tsx +++ b/packages/ui/src/components/OrganizationProfile/EnrollmentBadge.tsx @@ -7,6 +7,7 @@ const badgeLabelsMap: Record { @@ -15,7 +16,11 @@ export const EnrollmentBadge = (props: { organizationDomain: OrganizationDomainR return null; } - if (!(organizationDomain.verification && organizationDomain.verification.status === 'verified')) { + const isVerified = + organizationDomain.ownershipVerification?.status === 'verified' || + organizationDomain.verification?.status === 'verified'; + + if (!isVerified) { return ( { const { - isLoading, + isLoading: isLoadingEnterpriseConnection, enterpriseConnection, organizationEnterpriseConnection, testRuns, - mutations, - primaryEmailAddress, + enterpriseConnectionMutations, } = useOrganizationEnterpriseConnection(); + const { + isLoading: isLoadingOrganizationDomains, + data: organizationDomains, + createDomain, + prepareOwnershipVerification, + attemptOwnershipVerification, + revalidate, + } = __internal_useOrganizationDomains({ + enrollmentMode: 'enterprise_sso', + }); + + const organizationDomainMutations = React.useMemo( + () => ({ createDomain, prepareOwnershipVerification, attemptOwnershipVerification, revalidate }), + [createDomain, prepareOwnershipVerification, attemptOwnershipVerification, revalidate], + ); + // Gate loading above the provider so the context never observes a loading state. - if (isLoading) { + if (isLoadingEnterpriseConnection || isLoadingOrganizationDomains) { return ; } @@ -43,8 +60,9 @@ const OrganizationSecurityPageContent = ({ contentRef }: OrganizationSecurityPag testRuns={testRuns} enterpriseConnection={enterpriseConnection} contentRef={contentRef} - mutations={mutations} - primaryEmailAddress={primaryEmailAddress} + enterpriseConnectionMutations={enterpriseConnectionMutations} + organizationDomainMutations={organizationDomainMutations} + organizationDomains={organizationDomains} /> ); diff --git a/packages/ui/src/customizables/elementDescriptors.ts b/packages/ui/src/customizables/elementDescriptors.ts index adb4644e66e..fbec96906e7 100644 --- a/packages/ui/src/customizables/elementDescriptors.ts +++ b/packages/ui/src/customizables/elementDescriptors.ts @@ -568,6 +568,13 @@ export const APPEARANCE_KEYS = containsAllElementsConfigKeys([ 'configureSSOVerifyDomainErrorIcon', 'configureSSOVerifyDomainErrorTitle', 'configureSSOVerifyDomainErrorSubtitle', + 'configureSSOVerifyDomainList', + 'configureSSOVerifyDomainSuggestion', + 'configureSSOVerifyDomainCard', + 'configureSSOVerifyDomainCardBadge', + 'configureSSOVerifyDomainCardRemoveButton', + 'configureSSOVerifyDomainCardTxtRecord', + 'configureSSOVerifyDomainCardTxtRecordValue', 'configureSSOEmailVerificationForm', 'configureSSOEmailVerificationIcon', 'configureSSOEmailVerificationTitle', diff --git a/packages/ui/src/elements/contexts/index.tsx b/packages/ui/src/elements/contexts/index.tsx index 8da8277458c..3097de03351 100644 --- a/packages/ui/src/elements/contexts/index.tsx +++ b/packages/ui/src/elements/contexts/index.tsx @@ -136,7 +136,7 @@ export type FlowMetadata = { | 'methodSelectionMFA' | 'provideEmail' | 'selectProvider' - | 'verifyDomain' + | 'organizationDomains' | 'configureCreateApp' | 'configureMapAttributes' | 'testSso' diff --git a/packages/ui/src/internal/appearance.ts b/packages/ui/src/internal/appearance.ts index 512d0dd8e5c..84c17bb4ab6 100644 --- a/packages/ui/src/internal/appearance.ts +++ b/packages/ui/src/internal/appearance.ts @@ -704,6 +704,13 @@ export type ElementsConfig = { configureSSOVerifyDomainErrorIcon: WithOptions; configureSSOVerifyDomainErrorTitle: WithOptions; configureSSOVerifyDomainErrorSubtitle: WithOptions; + configureSSOVerifyDomainList: WithOptions; + configureSSOVerifyDomainSuggestion: WithOptions; + configureSSOVerifyDomainCard: WithOptions<'verified' | 'unverified'>; + configureSSOVerifyDomainCardBadge: WithOptions<'verified' | 'unverified'>; + configureSSOVerifyDomainCardRemoveButton: WithOptions; + configureSSOVerifyDomainCardTxtRecord: WithOptions; + configureSSOVerifyDomainCardTxtRecordValue: WithOptions; configureSSOEmailVerificationForm: WithOptions; configureSSOEmailVerificationIcon: WithOptions; configureSSOEmailVerificationTitle: WithOptions;