diff --git a/packages/clerk-js/src/core/resources/Organization.ts b/packages/clerk-js/src/core/resources/Organization.ts index c3c91cb4b73..6775aa8a93d 100644 --- a/packages/clerk-js/src/core/resources/Organization.ts +++ b/packages/clerk-js/src/core/resources/Organization.ts @@ -134,11 +134,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, diff --git a/packages/clerk-js/src/core/resources/OrganizationDomain.ts b/packages/clerk-js/src/core/resources/OrganizationDomain.ts index 9200f31388e..9bb81cab3df 100644 --- a/packages/clerk-js/src/core/resources/OrganizationDomain.ts +++ b/packages/clerk-js/src/core/resources/OrganizationDomain.ts @@ -1,6 +1,7 @@ import type { AttemptAffiliationVerificationParams, OrganizationDomainJSON, + OrganizationDomainOwnershipVerification, OrganizationDomainResource, OrganizationDomainVerification, OrganizationEnrollmentMode, @@ -17,6 +18,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; @@ -59,6 +62,20 @@ export class OrganizationDomain extends BaseResource implements OrganizationDoma }); }; + prepareOwnershipVerification = async (): Promise => { + return this._basePost({ + path: `/organizations/${this.organizationId}/domains/${this.id}/prepare_ownership_verification`, + method: 'POST', + }); + }; + + attemptOwnershipVerification = async (): Promise => { + return this._basePost({ + path: `/organizations/${this.organizationId}/domains/${this.id}/attempt_ownership_verification`, + method: 'POST', + }); + }; + updateEnrollmentMode = (params: UpdateEnrollmentModeParams): Promise => { return this._basePost({ path: `/organizations/${this.organizationId}/domains/${this.id}/update_enrollment_mode`, @@ -81,16 +98,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..71d61ff7fcb 100644 --- a/packages/localizations/src/ar-SA.ts +++ b/packages/localizations/src/ar-SA.ts @@ -196,29 +196,12 @@ 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: 'عنوان البريد الإلكتروني الذي تم التحقق منه', - }, - }, - domainTaken: { - title: 'هذا النطاق ({{domain}}) لديه بالفعل اتصال SSO', - subtitle: 'تواصل مع مسؤول التطبيق للحصول على الوصول من خلال الاتصال الحالي.', - }, + organizationDomainsStep: { + title: 'إضافة نطاقات SSO', + subtitle: 'أضِف نطاقات مؤسستك المستخدمة لتسجيل الدخول وتحقّق من ملكيتها.', + formFieldLabel__domain: 'النطاقات', + formFieldInputPlaceholder__domain: 'اكتب نطاقك هنا وانقر على إضافة للبدء', + formButtonPrimary__add: 'إضافة', }, }, createOrganization: { diff --git a/packages/localizations/src/be-BY.ts b/packages/localizations/src/be-BY.ts index b24fe2a2a0e..6abc04fc7c3 100644 --- a/packages/localizations/src/be-BY.ts +++ b/packages/localizations/src/be-BY.ts @@ -196,29 +196,12 @@ 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: 'Пацверджаны адрас электроннай пошты', - }, - }, - domainTaken: { - title: 'Гэты дамен ({{domain}}) ужо мае SSO-падключэнне', - subtitle: 'Звяжыцеся з адміністратарам прыкладання, каб атрымаць доступ праз існуючае падключэнне.', - }, + organizationDomainsStep: { + title: 'Дадаць дамены SSO', + subtitle: 'Дадайце і пацвердзіце права ўласнасці на дамены, якія ваша арганізацыя выкарыстоўвае для ўваходу.', + formFieldLabel__domain: 'Дамены', + formFieldInputPlaceholder__domain: 'Увядзіце свой дамен тут і націсніце «Дадаць», каб пачаць', + formButtonPrimary__add: 'Дадаць', }, }, createOrganization: { diff --git a/packages/localizations/src/bg-BG.ts b/packages/localizations/src/bg-BG.ts index 1f4fd93ba1e..1438bd87c4d 100644 --- a/packages/localizations/src/bg-BG.ts +++ b/packages/localizations/src/bg-BG.ts @@ -197,29 +197,12 @@ 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: 'Потвърден имейл адрес', - }, - }, - domainTaken: { - title: 'Този домейн ({{domain}}) вече има SSO връзка', - subtitle: 'Свържете се с администратора на приложението, за да получите достъп чрез съществуващата връзка.', - }, + organizationDomainsStep: { + title: 'Добавяне на SSO домейни', + subtitle: 'Добавете и потвърдете собствеността върху домейните, които вашата организация използва за вход.', + formFieldLabel__domain: 'Домейни', + formFieldInputPlaceholder__domain: 'Въведете домейна си тук и щракнете върху добавяне, за да започнете', + formButtonPrimary__add: 'Добави', }, }, createOrganization: { diff --git a/packages/localizations/src/bn-IN.ts b/packages/localizations/src/bn-IN.ts index 4b962b33ef1..b671fb43581 100644 --- a/packages/localizations/src/bn-IN.ts +++ b/packages/localizations/src/bn-IN.ts @@ -202,29 +202,12 @@ 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: 'যাচাইকৃত ইমেইল ঠিকানা', - }, - }, - domainTaken: { - title: 'এই ডোমেইনে ({{domain}}) ইতিমধ্যে একটি SSO সংযোগ রয়েছে', - subtitle: 'বিদ্যমান সংযোগের মাধ্যমে অ্যাক্সেস পেতে অ্যাপ্লিকেশন প্রশাসকের সাথে যোগাযোগ করুন।', - }, + organizationDomainsStep: { + title: 'SSO ডোমেইন যোগ করুন', + subtitle: 'আপনার প্রতিষ্ঠান সাইন ইন করতে যে ডোমেইনগুলি ব্যবহার করে তার মালিকানা যোগ করুন এবং যাচাই করুন।', + formFieldLabel__domain: 'ডোমেইন', + formFieldInputPlaceholder__domain: 'আপনার ডোমেইন এখানে টাইপ করুন এবং শুরু করতে যোগ করুন-এ ক্লিক করুন', + formButtonPrimary__add: 'যোগ করুন', }, }, createOrganization: { diff --git a/packages/localizations/src/ca-ES.ts b/packages/localizations/src/ca-ES.ts index 3b5339884e9..c881c79d4d7 100644 --- a/packages/localizations/src/ca-ES.ts +++ b/packages/localizations/src/ca-ES.ts @@ -203,29 +203,12 @@ 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', - }, - }, - 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.", - }, + 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', }, }, createOrganization: { diff --git a/packages/localizations/src/cs-CZ.ts b/packages/localizations/src/cs-CZ.ts index 3a5c09c266f..4703a617094 100644 --- a/packages/localizations/src/cs-CZ.ts +++ b/packages/localizations/src/cs-CZ.ts @@ -200,29 +200,12 @@ 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', - }, - }, - 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í.', - }, + 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', }, }, createOrganization: { diff --git a/packages/localizations/src/da-DK.ts b/packages/localizations/src/da-DK.ts index 53f74d0a66f..71bea5d6eca 100644 --- a/packages/localizations/src/da-DK.ts +++ b/packages/localizations/src/da-DK.ts @@ -196,29 +196,12 @@ 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', - }, - }, - domainTaken: { - title: 'Dette domæne ({{domain}}) har allerede en SSO-forbindelse', - subtitle: 'Kontakt applikationens administrator for at få adgang via den eksisterende forbindelse.', - }, + 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', }, }, createOrganization: { diff --git a/packages/localizations/src/de-DE.ts b/packages/localizations/src/de-DE.ts index 5ebc5a9025c..52e7805db0c 100644 --- a/packages/localizations/src/de-DE.ts +++ b/packages/localizations/src/de-DE.ts @@ -203,30 +203,13 @@ 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', - }, - }, - 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.', - }, + 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', }, }, createOrganization: { diff --git a/packages/localizations/src/el-GR.ts b/packages/localizations/src/el-GR.ts index 0ae1f413953..8023e5ee04a 100644 --- a/packages/localizations/src/el-GR.ts +++ b/packages/localizations/src/el-GR.ts @@ -196,30 +196,13 @@ 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', - }, - }, - domainTaken: { - title: 'Αυτός ο τομέας ({{domain}}) διαθέτει ήδη σύνδεση SSO', - subtitle: - 'Επικοινωνήστε με τον διαχειριστή της εφαρμογής για να αποκτήσετε πρόσβαση μέσω της υπάρχουσας σύνδεσης.', - }, + organizationDomainsStep: { + title: 'Προσθήκη τομέων SSO', + subtitle: 'Προσθέστε και επαληθεύστε την κυριότητα των τομέων που χρησιμοποιεί ο οργανισμός σας για σύνδεση.', + formFieldLabel__domain: 'Τομείς', + formFieldInputPlaceholder__domain: + 'Πληκτρολογήστε τον τομέα σας εδώ και κάντε κλικ στο «Προσθήκη» για να ξεκινήσετε', + formButtonPrimary__add: 'Προσθήκη', }, }, createOrganization: { diff --git a/packages/localizations/src/en-GB.ts b/packages/localizations/src/en-GB.ts index e3123b6333a..97465d6bb07 100644 --- a/packages/localizations/src/en-GB.ts +++ b/packages/localizations/src/en-GB.ts @@ -196,29 +196,12 @@ 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', - }, - }, - domainTaken: { - title: 'This domain ({{domain}}) already has an SSO connection', - subtitle: "Contact the application's administrator to get access through the existing connection.", - }, + 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', }, }, createOrganization: { @@ -540,7 +523,7 @@ export const enGB: LocalizationResource = { resendButton: "Didn't receive a code? Resend", subtitle: 'The domain {{domainName}} needs to be verified via email.', subtitleVerificationCodeScreen: 'A verification code was sent to {{emailAddress}}. Enter the code to continue.', - title: 'Verify domain', + title: 'Verify domains', }, }, organizationSwitcher: { diff --git a/packages/localizations/src/en-US.ts b/packages/localizations/src/en-US.ts index 5ac237a6cd2..27156b1a85d 100644 --- a/packages/localizations/src/en-US.ts +++ b/packages/localizations/src/en-US.ts @@ -259,29 +259,23 @@ 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', + 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', @@ -872,6 +866,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: { @@ -1094,7 +1089,7 @@ export const enUS: LocalizationResource = { resendButton: "Didn't receive a code? Resend", subtitle: 'The domain {{domainName}} needs to be verified via email.', subtitleVerificationCodeScreen: 'A verification code was sent to {{emailAddress}}. Enter the code to continue.', - title: 'Verify domain', + title: 'Verify domains', }, }, organizationSwitcher: { diff --git a/packages/localizations/src/es-CR.ts b/packages/localizations/src/es-CR.ts index 5c193ce8b3d..a1401742071 100644 --- a/packages/localizations/src/es-CR.ts +++ b/packages/localizations/src/es-CR.ts @@ -196,29 +196,12 @@ 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', - }, - }, - 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.', - }, + 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', }, }, createOrganization: { diff --git a/packages/localizations/src/es-ES.ts b/packages/localizations/src/es-ES.ts index 2ffb8d21c1b..5ba97d00b1e 100644 --- a/packages/localizations/src/es-ES.ts +++ b/packages/localizations/src/es-ES.ts @@ -202,30 +202,12 @@ 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', - }, - }, - 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.', - }, + 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', }, }, createOrganization: { diff --git a/packages/localizations/src/es-MX.ts b/packages/localizations/src/es-MX.ts index 1b1fa64ac67..19a0b62049e 100644 --- a/packages/localizations/src/es-MX.ts +++ b/packages/localizations/src/es-MX.ts @@ -197,29 +197,12 @@ 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', - }, - }, - 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.', - }, + 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', }, }, createOrganization: { diff --git a/packages/localizations/src/es-UY.ts b/packages/localizations/src/es-UY.ts index bd16067ed07..d1196badeb7 100644 --- a/packages/localizations/src/es-UY.ts +++ b/packages/localizations/src/es-UY.ts @@ -196,29 +196,12 @@ 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', - }, - }, - 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.', - }, + 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', }, }, createOrganization: { diff --git a/packages/localizations/src/fa-IR.ts b/packages/localizations/src/fa-IR.ts index 16b8afa5448..ae41a8b1ccd 100644 --- a/packages/localizations/src/fa-IR.ts +++ b/packages/localizations/src/fa-IR.ts @@ -201,29 +201,12 @@ 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: 'آدرس ایمیل تأیید شده', - }, - }, - domainTaken: { - title: 'این دامنه ({{domain}}) قبلاً یک اتصال SSO دارد', - subtitle: 'برای دسترسی از طریق اتصال موجود، با مدیر برنامه تماس بگیرید.', - }, + organizationDomainsStep: { + title: 'افزودن دامنه‌های SSO', + subtitle: 'مالکیت دامنه‌هایی را که سازمان شما برای ورود استفاده می‌کند، اضافه و تأیید کنید.', + formFieldLabel__domain: 'دامنه‌ها', + formFieldInputPlaceholder__domain: 'دامنه خود را اینجا تایپ کنید و برای شروع روی افزودن کلیک کنید', + formButtonPrimary__add: 'افزودن', }, }, createOrganization: { diff --git a/packages/localizations/src/fi-FI.ts b/packages/localizations/src/fi-FI.ts index 7f5fa9eeb14..a7295725f7e 100644 --- a/packages/localizations/src/fi-FI.ts +++ b/packages/localizations/src/fi-FI.ts @@ -224,30 +224,12 @@ 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', - }, - }, - 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.', - }, + 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ää', }, }, createOrganization: { diff --git a/packages/localizations/src/fr-FR.ts b/packages/localizations/src/fr-FR.ts index a33f1289508..7c59661f93d 100644 --- a/packages/localizations/src/fr-FR.ts +++ b/packages/localizations/src/fr-FR.ts @@ -205,29 +205,12 @@ 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', - }, - }, - 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.", - }, + 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', }, }, createOrganization: { diff --git a/packages/localizations/src/he-IL.ts b/packages/localizations/src/he-IL.ts index 6dbf545ab7e..5975fade4bf 100644 --- a/packages/localizations/src/he-IL.ts +++ b/packages/localizations/src/he-IL.ts @@ -196,29 +196,12 @@ 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: 'כתובת אימייל מאומתת', - }, - }, - domainTaken: { - title: 'לדומיין הזה ({{domain}}) כבר יש חיבור SSO', - subtitle: 'צור קשר עם מנהל היישום כדי לקבל גישה דרך החיבור הקיים.', - }, + organizationDomainsStep: { + title: 'הוספת דומיינים של SSO', + subtitle: 'הוסף ואמת בעלות על הדומיינים שהארגון שלך משתמש בהם כדי להתחבר.', + formFieldLabel__domain: 'דומיינים', + formFieldInputPlaceholder__domain: 'הקלד את הדומיין שלך כאן ולחץ על הוסף כדי להתחיל', + formButtonPrimary__add: 'הוסף', }, }, createOrganization: { diff --git a/packages/localizations/src/hi-IN.ts b/packages/localizations/src/hi-IN.ts index 66a57251752..5c52d4ba51d 100644 --- a/packages/localizations/src/hi-IN.ts +++ b/packages/localizations/src/hi-IN.ts @@ -202,29 +202,12 @@ 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: 'सत्यापित ईमेल पता', - }, - }, - domainTaken: { - title: 'इस डोमेन ({{domain}}) में पहले से ही एक SSO कनेक्शन है', - subtitle: 'मौजूदा कनेक्शन के माध्यम से एक्सेस प्राप्त करने के लिए एप्लिकेशन के व्यवस्थापक से संपर्क करें।', - }, + organizationDomainsStep: { + title: 'SSO डोमेन जोड़ें', + subtitle: 'अपने संगठन द्वारा साइन इन के लिए उपयोग किए जाने वाले डोमेन का स्वामित्व जोड़ें और सत्यापित करें।', + formFieldLabel__domain: 'डोमेन', + formFieldInputPlaceholder__domain: 'अपना डोमेन यहाँ टाइप करें और शुरू करने के लिए जोड़ें पर क्लिक करें', + formButtonPrimary__add: 'जोड़ें', }, }, createOrganization: { diff --git a/packages/localizations/src/hr-HR.ts b/packages/localizations/src/hr-HR.ts index 1b31cfebbfa..fb4d168afd3 100644 --- a/packages/localizations/src/hr-HR.ts +++ b/packages/localizations/src/hr-HR.ts @@ -225,29 +225,12 @@ 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', - }, - }, - domainTaken: { - title: 'Ova domena ({{domain}}) već ima SSO vezu', - subtitle: 'Kontaktirajte administratora aplikacije kako biste dobili pristup putem postojeće veze.', - }, + 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', }, }, createOrganization: { diff --git a/packages/localizations/src/hu-HU.ts b/packages/localizations/src/hu-HU.ts index 3714f35e4a0..8a868c184b4 100644 --- a/packages/localizations/src/hu-HU.ts +++ b/packages/localizations/src/hu-HU.ts @@ -225,29 +225,13 @@ 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', - }, - }, - 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.', - }, + 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', }, }, createOrganization: { diff --git a/packages/localizations/src/id-ID.ts b/packages/localizations/src/id-ID.ts index 1483a6307e2..67767f5aa0a 100644 --- a/packages/localizations/src/id-ID.ts +++ b/packages/localizations/src/id-ID.ts @@ -196,29 +196,12 @@ 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', - }, - }, - domainTaken: { - title: 'Domain ini ({{domain}}) sudah memiliki koneksi SSO', - subtitle: 'Hubungi administrator aplikasi untuk mendapatkan akses melalui koneksi yang ada.', - }, + 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', }, }, createOrganization: { diff --git a/packages/localizations/src/is-IS.ts b/packages/localizations/src/is-IS.ts index 304827137bb..90a0ebac535 100644 --- a/packages/localizations/src/is-IS.ts +++ b/packages/localizations/src/is-IS.ts @@ -224,29 +224,12 @@ 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', - }, - }, - 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.', - }, + 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ð', }, }, createOrganization: { diff --git a/packages/localizations/src/it-IT.ts b/packages/localizations/src/it-IT.ts index 619225ef990..ea61fa37d42 100644 --- a/packages/localizations/src/it-IT.ts +++ b/packages/localizations/src/it-IT.ts @@ -202,30 +202,12 @@ 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', - }, - }, - domainTaken: { - title: 'Questo dominio ({{domain}}) ha già una connessione SSO', - subtitle: - "Contatta l'amministratore dell'applicazione per ottenere l'accesso tramite la connessione esistente.", - }, + 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', }, }, createOrganization: { diff --git a/packages/localizations/src/ja-JP.ts b/packages/localizations/src/ja-JP.ts index 35ecb633605..4d055ddbbc7 100644 --- a/packages/localizations/src/ja-JP.ts +++ b/packages/localizations/src/ja-JP.ts @@ -207,29 +207,12 @@ 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: '確認済みメールアドレス', - }, - }, - domainTaken: { - title: 'このドメイン ({{domain}}) にはすでに SSO 接続が存在します', - subtitle: '既存の接続を通じてアクセスを取得するには、アプリケーションの管理者にお問い合わせください。', - }, + organizationDomainsStep: { + title: 'SSO ドメインを追加', + subtitle: '組織がサインインに使用するドメインを追加し、所有権を確認します。', + formFieldLabel__domain: 'ドメイン', + formFieldInputPlaceholder__domain: 'ここにドメインを入力し、「追加」をクリックして開始します', + formButtonPrimary__add: '追加', }, }, createOrganization: { diff --git a/packages/localizations/src/kk-KZ.ts b/packages/localizations/src/kk-KZ.ts index ce4c0f5a143..277812f9838 100644 --- a/packages/localizations/src/kk-KZ.ts +++ b/packages/localizations/src/kk-KZ.ts @@ -196,29 +196,12 @@ 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: 'Расталған электрондық пошта мекенжайы', - }, - }, - domainTaken: { - title: 'Бұл доменде ({{domain}}) бұрыннан SSO байланысы бар', - subtitle: 'Қолданыстағы байланыс арқылы кіруге қол жеткізу үшін қолданба әкімшісіне хабарласыңыз.', - }, + organizationDomainsStep: { + title: 'SSO домендерін қосу', + subtitle: 'Ұйымыңыз кіру үшін пайдаланатын домендердің меншігін қосып, растаңыз.', + formFieldLabel__domain: 'Домендер', + formFieldInputPlaceholder__domain: 'Доменіңізді осы жерге енгізіп, бастау үшін «Қосу» түймесін басыңыз', + formButtonPrimary__add: 'Қосу', }, }, createOrganization: { diff --git a/packages/localizations/src/ko-KR.ts b/packages/localizations/src/ko-KR.ts index 38e8112a117..1cd93574830 100644 --- a/packages/localizations/src/ko-KR.ts +++ b/packages/localizations/src/ko-KR.ts @@ -203,29 +203,12 @@ 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: '확인된 이메일 주소', - }, - }, - domainTaken: { - title: '이 도메인 ({{domain}}) 에는 이미 SSO 연결이 있습니다', - subtitle: '기존 연결을 통해 접근 권한을 받으려면 애플리케이션 관리자에게 문의하세요.', - }, + organizationDomainsStep: { + title: 'SSO 도메인 추가', + subtitle: '조직에서 로그인에 사용하는 도메인의 소유권을 추가하고 확인하세요.', + formFieldLabel__domain: '도메인', + formFieldInputPlaceholder__domain: '여기에 도메인을 입력하고 추가를 클릭하여 시작하세요', + formButtonPrimary__add: '추가', }, }, createOrganization: { diff --git a/packages/localizations/src/mn-MN.ts b/packages/localizations/src/mn-MN.ts index 4935f8b22aa..c6b862deeee 100644 --- a/packages/localizations/src/mn-MN.ts +++ b/packages/localizations/src/mn-MN.ts @@ -196,29 +196,12 @@ 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: 'Баталгаажсан и-мэйл хаяг', - }, - }, - domainTaken: { - title: 'Энэ домейн ({{domain}}) аль хэдийн SSO холболттой байна', - subtitle: 'Одоо байгаа холболтоор дамжуулан хандах эрх авахын тулд програмын администратортой холбогдоно уу.', - }, + organizationDomainsStep: { + title: 'SSO домэйн нэмэх', + subtitle: 'Танай байгууллагын нэвтрэхэд ашигладаг домэйнуудын эзэмшлийг нэмж баталгаажуулна уу.', + formFieldLabel__domain: 'Домэйн', + formFieldInputPlaceholder__domain: 'Домэйнээ энд бичээд эхлэхийн тулд нэмэх дээр дарна уу', + formButtonPrimary__add: 'Нэмэх', }, }, createOrganization: { diff --git a/packages/localizations/src/ms-MY.ts b/packages/localizations/src/ms-MY.ts index 4d2afdf435b..72b8b9019a4 100644 --- a/packages/localizations/src/ms-MY.ts +++ b/packages/localizations/src/ms-MY.ts @@ -204,29 +204,12 @@ 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', - }, - }, - domainTaken: { - title: 'Domain ini ({{domain}}) sudah mempunyai sambungan SSO', - subtitle: 'Hubungi pentadbir aplikasi untuk mendapatkan akses melalui sambungan sedia ada.', - }, + 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', }, }, createOrganization: { diff --git a/packages/localizations/src/nb-NO.ts b/packages/localizations/src/nb-NO.ts index 0833499c8f4..43688e66e17 100644 --- a/packages/localizations/src/nb-NO.ts +++ b/packages/localizations/src/nb-NO.ts @@ -225,29 +225,12 @@ 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', - }, - }, - domainTaken: { - title: 'Dette domenet ({{domain}}) har allerede en SSO-tilkobling', - subtitle: 'Kontakt programmets administrator for å få tilgang via den eksisterende tilkoblingen.', - }, + 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', }, }, createOrganization: { diff --git a/packages/localizations/src/nl-BE.ts b/packages/localizations/src/nl-BE.ts index f836c45b447..999630dc56d 100644 --- a/packages/localizations/src/nl-BE.ts +++ b/packages/localizations/src/nl-BE.ts @@ -196,30 +196,12 @@ 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', - }, - }, - 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.', - }, + 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', }, }, createOrganization: { diff --git a/packages/localizations/src/nl-NL.ts b/packages/localizations/src/nl-NL.ts index 1ce7af52f1f..93cf0fbc653 100644 --- a/packages/localizations/src/nl-NL.ts +++ b/packages/localizations/src/nl-NL.ts @@ -196,30 +196,12 @@ 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', - }, - }, - 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.', - }, + 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', }, }, createOrganization: { diff --git a/packages/localizations/src/pl-PL.ts b/packages/localizations/src/pl-PL.ts index ad5cad86f3e..b282a11f0df 100644 --- a/packages/localizations/src/pl-PL.ts +++ b/packages/localizations/src/pl-PL.ts @@ -196,29 +196,12 @@ 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', - }, - }, - 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.', - }, + 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', }, }, createOrganization: { diff --git a/packages/localizations/src/pt-BR.ts b/packages/localizations/src/pt-BR.ts index 10fac051b9e..f712e07d3da 100644 --- a/packages/localizations/src/pt-BR.ts +++ b/packages/localizations/src/pt-BR.ts @@ -203,29 +203,12 @@ 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', - }, - }, - 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.', - }, + 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', }, }, createOrganization: { diff --git a/packages/localizations/src/pt-PT.ts b/packages/localizations/src/pt-PT.ts index 959a9b93ef9..3ca468f056a 100644 --- a/packages/localizations/src/pt-PT.ts +++ b/packages/localizations/src/pt-PT.ts @@ -204,29 +204,12 @@ 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', - }, - }, - 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.', - }, + 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', }, }, createOrganization: { diff --git a/packages/localizations/src/ro-RO.ts b/packages/localizations/src/ro-RO.ts index 8ed87bb076a..3d9f206d90e 100644 --- a/packages/localizations/src/ro-RO.ts +++ b/packages/localizations/src/ro-RO.ts @@ -202,29 +202,13 @@ 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ă', - }, - }, - domainTaken: { - title: 'Acest domeniu ({{domain}}) are deja o conexiune SSO', - subtitle: 'Contactează administratorul aplicației pentru a obține acces prin conexiunea existentă.', - }, + 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ă', }, }, createOrganization: { diff --git a/packages/localizations/src/ru-RU.ts b/packages/localizations/src/ru-RU.ts index 8988d668748..e4166e7300a 100644 --- a/packages/localizations/src/ru-RU.ts +++ b/packages/localizations/src/ru-RU.ts @@ -196,29 +196,12 @@ 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: 'Подтверждённый адрес электронной почты', - }, - }, - domainTaken: { - title: 'Этот домен ({{domain}}) уже имеет SSO-подключение', - subtitle: 'Свяжитесь с администратором приложения, чтобы получить доступ через существующее подключение.', - }, + organizationDomainsStep: { + title: 'Добавить домены SSO', + subtitle: 'Добавьте и подтвердите право собственности на домены, которые ваша организация использует для входа.', + formFieldLabel__domain: 'Домены', + formFieldInputPlaceholder__domain: 'Введите домен здесь и нажмите «Добавить», чтобы начать', + formButtonPrimary__add: 'Добавить', }, }, createOrganization: { diff --git a/packages/localizations/src/sk-SK.ts b/packages/localizations/src/sk-SK.ts index a8c250279c4..e8903f07fec 100644 --- a/packages/localizations/src/sk-SK.ts +++ b/packages/localizations/src/sk-SK.ts @@ -196,30 +196,12 @@ 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', - }, - }, - 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.', - }, + 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ť', }, }, createOrganization: { diff --git a/packages/localizations/src/sr-RS.ts b/packages/localizations/src/sr-RS.ts index db6dcdb34b4..0fd7170dc9d 100644 --- a/packages/localizations/src/sr-RS.ts +++ b/packages/localizations/src/sr-RS.ts @@ -196,29 +196,12 @@ 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', - }, - }, - domainTaken: { - title: 'Ovaj domen ({{domain}}) već ima SSO konekciju', - subtitle: 'Kontaktirajte administratora aplikacije da biste dobili pristup putem postojeće konekcije.', - }, + 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', }, }, createOrganization: { diff --git a/packages/localizations/src/sv-SE.ts b/packages/localizations/src/sv-SE.ts index 986421ecbc5..786d52e825e 100644 --- a/packages/localizations/src/sv-SE.ts +++ b/packages/localizations/src/sv-SE.ts @@ -196,29 +196,12 @@ 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', - }, - }, - 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.', - }, + 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', }, }, createOrganization: { diff --git a/packages/localizations/src/ta-IN.ts b/packages/localizations/src/ta-IN.ts index f4557c462e4..291fb663553 100644 --- a/packages/localizations/src/ta-IN.ts +++ b/packages/localizations/src/ta-IN.ts @@ -204,29 +204,12 @@ 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: 'சரிபார்க்கப்பட்ட மின்னஞ்சல் முகவரி', - }, - }, - domainTaken: { - title: 'இந்த டொமைனுக்கு ({{domain}}) ஏற்கனவே SSO இணைப்பு உள்ளது', - subtitle: 'ஏற்கனவே உள்ள இணைப்பின் வழியாக அணுகலைப் பெற, பயன்பாட்டின் நிர்வாகியைத் தொடர்பு கொள்ளவும்.', - }, + organizationDomainsStep: { + title: 'SSO டொமைன்களைச் சேர்க்கவும்', + subtitle: 'உங்கள் நிறுவனம் உள்நுழைய பயன்படுத்தும் டொமைன்களின் உரிமையைச் சேர்த்து சரிபார்க்கவும்.', + formFieldLabel__domain: 'டொமைன்கள்', + formFieldInputPlaceholder__domain: 'உங்கள் டொமைனை இங்கே தட்டச்சு செய்து, தொடங்க சேர் என்பதைக் கிளிக் செய்யவும்', + formButtonPrimary__add: 'சேர்', }, }, createOrganization: { diff --git a/packages/localizations/src/te-IN.ts b/packages/localizations/src/te-IN.ts index b13243dfab6..f193f57c930 100644 --- a/packages/localizations/src/te-IN.ts +++ b/packages/localizations/src/te-IN.ts @@ -203,29 +203,12 @@ 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: 'ధృవీకరించబడిన ఇమెయిల్ చిరునామా', - }, - }, - domainTaken: { - title: 'ఈ డొమైన్‌కి ({{domain}}) ఇప్పటికే SSO కనెక్షన్ ఉంది', - subtitle: 'ఇప్పటికే ఉన్న కనెక్షన్ ద్వారా యాక్సెస్ పొందడానికి అప్లికేషన్ నిర్వాహకుడిని సంప్రదించండి.', - }, + organizationDomainsStep: { + title: 'SSO డొమైన్‌లను జోడించండి', + subtitle: 'మీ సంస్థ సైన్ ఇన్ చేయడానికి ఉపయోగించే డొమైన్‌ల యాజమాన్యాన్ని జోడించి ధృవీకరించండి.', + formFieldLabel__domain: 'డొమైన్‌లు', + formFieldInputPlaceholder__domain: 'మీ డొమైన్‌ను ఇక్కడ టైప్ చేసి, ప్రారంభించడానికి జోడించు క్లిక్ చేయండి', + formButtonPrimary__add: 'జోడించు', }, }, createOrganization: { diff --git a/packages/localizations/src/th-TH.ts b/packages/localizations/src/th-TH.ts index 6a8ca5f679e..42568239e52 100644 --- a/packages/localizations/src/th-TH.ts +++ b/packages/localizations/src/th-TH.ts @@ -200,29 +200,12 @@ 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: 'ที่อยู่อีเมลที่ยืนยันแล้ว', - }, - }, - domainTaken: { - title: 'โดเมนนี้ ({{domain}}) มีการเชื่อมต่อ SSO อยู่แล้ว', - subtitle: 'ติดต่อผู้ดูแลระบบของแอปพลิเคชันเพื่อขอเข้าถึงผ่านการเชื่อมต่อที่มีอยู่', - }, + organizationDomainsStep: { + title: 'เพิ่มโดเมน SSO', + subtitle: 'เพิ่มและยืนยันความเป็นเจ้าของโดเมนที่องค์กรของคุณใช้ในการลงชื่อเข้าใช้', + formFieldLabel__domain: 'โดเมน', + formFieldInputPlaceholder__domain: 'พิมพ์โดเมนของคุณที่นี่แล้วคลิกเพิ่มเพื่อเริ่มต้น', + formButtonPrimary__add: 'เพิ่ม', }, }, createOrganization: { diff --git a/packages/localizations/src/tr-TR.ts b/packages/localizations/src/tr-TR.ts index 939380a2176..da9e2309628 100644 --- a/packages/localizations/src/tr-TR.ts +++ b/packages/localizations/src/tr-TR.ts @@ -196,29 +196,12 @@ 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', - }, - }, - 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.', - }, + 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', }, }, createOrganization: { diff --git a/packages/localizations/src/uk-UA.ts b/packages/localizations/src/uk-UA.ts index 9a036c44183..0c18322aa30 100644 --- a/packages/localizations/src/uk-UA.ts +++ b/packages/localizations/src/uk-UA.ts @@ -196,29 +196,12 @@ 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: 'Підтверджена адреса електронної пошти', - }, - }, - domainTaken: { - title: "Цей домен ({{domain}}) уже має з'єднання SSO", - subtitle: "Зверніться до адміністратора програми, щоб отримати доступ через існуюче з'єднання.", - }, + organizationDomainsStep: { + title: 'Додати домени SSO', + subtitle: 'Додайте та підтвердьте право власності на домени, які ваша організація використовує для входу.', + formFieldLabel__domain: 'Домени', + formFieldInputPlaceholder__domain: 'Введіть свій домен тут і натисніть «Додати», щоб почати', + formButtonPrimary__add: 'Додати', }, }, createOrganization: { @@ -539,7 +522,7 @@ export const ukUA: LocalizationResource = { resendButton: "Didn't receive a code? Resend", subtitle: 'The domain {{domainName}} needs to be verified via email.', subtitleVerificationCodeScreen: 'A verification code was sent to {{emailAddress}}. Enter the code to continue.', - title: 'Verify domain', + title: 'Verify domains', }, }, organizationSwitcher: { diff --git a/packages/localizations/src/utils/enUS_v4.ts b/packages/localizations/src/utils/enUS_v4.ts index a2958700f55..549e3aac013 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', }, }, @@ -591,7 +592,7 @@ export const enUS_v4: any = { 'Add the domain to verify. Users with email addresses at this domain can join the organization automatically or request to join.', }, verifyDomainPage: { - title: 'Verify domain', + title: 'Verify domains', subtitle: 'The domain {{domainName}} needs to be verified via email.', subtitleVerificationCodeScreen: 'A verification code was sent to {{emailAddress}}. Enter the code to continue.', formTitle: 'Verification code', diff --git a/packages/localizations/src/vi-VN.ts b/packages/localizations/src/vi-VN.ts index 683236376df..8e98024880f 100644 --- a/packages/localizations/src/vi-VN.ts +++ b/packages/localizations/src/vi-VN.ts @@ -202,29 +202,12 @@ 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', - }, - }, - 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ó.', - }, + 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', }, }, createOrganization: { diff --git a/packages/localizations/src/zh-CN.ts b/packages/localizations/src/zh-CN.ts index ba3034405f5..eb0892f864d 100644 --- a/packages/localizations/src/zh-CN.ts +++ b/packages/localizations/src/zh-CN.ts @@ -196,29 +196,12 @@ 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: '已验证的电子邮件地址', - }, - }, - domainTaken: { - title: '此域名 ({{domain}}) 已存在 SSO 连接', - subtitle: '请联系应用程序管理员,通过现有连接获取访问权限。', - }, + organizationDomainsStep: { + title: '添加 SSO 域名', + subtitle: '添加并验证贵组织用于登录的域名的所有权。', + formFieldLabel__domain: '域名', + formFieldInputPlaceholder__domain: '在此输入您的域名,然后点击添加即可开始', + formButtonPrimary__add: '添加', }, }, createOrganization: { diff --git a/packages/localizations/src/zh-TW.ts b/packages/localizations/src/zh-TW.ts index 3740bc4fc6f..75989c74348 100644 --- a/packages/localizations/src/zh-TW.ts +++ b/packages/localizations/src/zh-TW.ts @@ -202,29 +202,12 @@ 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: '已驗證的電子郵件地址', - }, - }, - domainTaken: { - title: '此網域 ({{domain}}) 已存在 SSO 連線', - subtitle: '請聯絡應用程式管理員,透過現有連線取得存取權限。', - }, + organizationDomainsStep: { + title: '新增 SSO 網域', + subtitle: '新增並驗證貴組織用於登入的網域的所有權。', + formFieldLabel__domain: '網域', + formFieldInputPlaceholder__domain: '在此輸入您的網域,然後點選新增即可開始', + formButtonPrimary__add: '新增', }, }, 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..5d73b525c74 --- /dev/null +++ b/packages/shared/src/react/hooks/useOrganizationDomains.tsx @@ -0,0 +1,132 @@ +import { useCallback } from 'react'; + +import type { GetDomainsParams } from '../../types/organization'; +import type { OrganizationDomainResource, 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'; + +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 the given domain. The resolved domain's + * `ownershipVerification` carries the `txtRecordName` and `txtRecordValue`. + */ + prepareOwnershipVerification: (domain: OrganizationDomainResource) => Promise; + /** + * Resolves the published TXT record for the given domain to complete ownership verification. + */ + attemptOwnershipVerification: (domain: 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); + + // When organization gets created with enterprise_sso enrollment mode + // then promote the new domain and issue a TXT ownership challenge so it can be verified over DNS + if (created && enrollmentMode === 'enterprise_sso') { + created = await created.updateEnrollmentMode({ enrollmentMode: 'enterprise_sso' }); + created = await created.prepareOwnershipVerification(); + } + + await revalidate(); + return created; + }, + [organization, revalidate, enrollmentMode], + ); + + const prepareOwnershipVerification = useCallback( + async (domain: OrganizationDomainResource) => { + const prepared = await domain.prepareOwnershipVerification(); + await revalidate(); + return prepared; + }, + [revalidate], + ); + + const attemptOwnershipVerification = useCallback( + async (domain: OrganizationDomainResource) => { + const attempted = await domain.attemptOwnershipVerification(); + await revalidate(); + return attempted; + }, + [revalidate], + ); + + const response = query.data; + + 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 4b39d9cf8a3..c0e0e7e950b 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; diff --git a/packages/shared/src/types/localization.ts b/packages/shared/src/types/localization.ts index ba4e114c4b9..21a34a51ff9 100644 --- a/packages/shared/src/types/localization.ts +++ b/packages/shared/src/types/localization.ts @@ -1027,6 +1027,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'>; @@ -1321,29 +1322,23 @@ 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; + 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/organizationDomain.ts b/packages/shared/src/types/organizationDomain.ts index d9d72746975..5d4a5d92441 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 `prepareOwnershipVerification`. + */ + txtRecordName: string | null; + /** + * The exact value the org admin must publish in the TXT record. Present only immediately after `prepareOwnershipVerification`. + */ + 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. */ @@ -98,6 +154,18 @@ export interface OrganizationDomainResource extends ClerkResource { * @returns A promise that resolves to the updated `OrganizationDomain` object. */ attemptAffiliationVerification: (params: AttemptAffiliationVerificationParams) => Promise; + /** + * Begins the ownership verification flow by issuing a fresh TXT challenge for the domain. The returned domain's `ownershipVerification` carries the `txtRecordName` and `txtRecordValue` the org admin must publish. + * + * @returns A promise that resolves to the updated `OrganizationDomain` object. + */ + prepareOwnershipVerification: () => Promise; + /** + * Completes the ownership verification flow by resolving the published TXT record for the domain. + * + * @returns A promise that resolves to the updated `OrganizationDomain` object. + */ + attemptOwnershipVerification: () => Promise; /** * Deletes the Verified Domain. * diff --git a/packages/ui/src/components/ConfigureSSO/ConfigureSSO.tsx b/packages/ui/src/components/ConfigureSSO/ConfigureSSO.tsx index 05aaffd7cc4..5235158ac50 100644 --- a/packages/ui/src/components/ConfigureSSO/ConfigureSSO.tsx +++ b/packages/ui/src/components/ConfigureSSO/ConfigureSSO.tsx @@ -1,4 +1,4 @@ -import { useSession } from '@clerk/shared/react'; +import { __internal_useOrganizationDomains, useSession } from '@clerk/shared/react'; import type { ConfigureSSOProps } from '@clerk/shared/types'; import React from 'react'; @@ -10,7 +10,7 @@ import { ProfileCard } from '@/elements/ProfileCard'; import { ExclamationTriangle } from '@/icons'; import { Route, Switch } from '@/router'; -import { ConfigureSSOProvider } from './ConfigureSSOContext'; +import { ConfigureSSOProvider, type OrganizationDomainMutations } from './ConfigureSSOContext'; import { ConfigureSSONavbar } from './ConfigureSSONavbar'; import { ConfigureSSOSkeleton } from './ConfigureSSOSkeleton'; import { ConfigureSSOSteps } from './ConfigureSSOSteps'; @@ -46,18 +46,30 @@ const AuthenticatedContent = withCoreUserGuard(() => { export const ConfigureSSOContent = ({ contentRef }: { contentRef: React.RefObject }) => { const { - isLoading, + isLoading: isLoadingEnterpriseConnection, enterpriseConnection, organizationEnterpriseConnection, testRuns, - mutations, - primaryEmailAddress, + enterpriseConnectionMutations, } = useOrganizationEnterpriseConnection(); - // Gate loading one level above the provider so the context never observes a - // loading state. The single test-run source is part of this initial fetch - // when a connection exists at load, so a cold landing on the test step is - // covered by the full skeleton here. + 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 ; } @@ -69,8 +81,9 @@ export const ConfigureSSOContent = ({ contentRef }: { contentRef: React.RefObjec testRuns={testRuns} enterpriseConnection={enterpriseConnection} contentRef={contentRef} - mutations={mutations} - primaryEmailAddress={primaryEmailAddress} + enterpriseConnectionMutations={enterpriseConnectionMutations} + organizationDomainMutations={organizationDomainMutations} + organizationDomains={organizationDomains} > diff --git a/packages/ui/src/components/ConfigureSSO/ConfigureSSOContext.tsx b/packages/ui/src/components/ConfigureSSO/ConfigureSSOContext.tsx index f40526d138a..023dc72c13e 100644 --- a/packages/ui/src/components/ConfigureSSO/ConfigureSSOContext.tsx +++ b/packages/ui/src/components/ConfigureSSO/ConfigureSSOContext.tsx @@ -1,9 +1,16 @@ -import type { EmailAddressResource, EnterpriseConnectionResource } from '@clerk/shared/types'; +import type { EnterpriseConnectionResource, OrganizationDomainResource } 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: (domain: OrganizationDomainResource) => Promise; + attemptOwnershipVerification: (domain: OrganizationDomainResource) => Promise; + revalidate: () => Promise; +} + /** * Shared state for the ConfigureSSO wizard, persisted across steps. Everything * is sourced from the umbrella `useOrganizationEnterpriseConnection` hook one @@ -14,19 +21,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 +45,10 @@ export const ConfigureSSOProvider = ({ enterpriseConnection, organizationEnterpriseConnection, testRuns, + organizationDomains, contentRef, - mutations, - primaryEmailAddress, + enterpriseConnectionMutations, + organizationDomainMutations, children, }: PropsWithChildren): JSX.Element => { const value = React.useMemo( @@ -47,10 +57,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/ConfigureSSOSteps.tsx b/packages/ui/src/components/ConfigureSSO/ConfigureSSOSteps.tsx index b43e766f14f..b78a4590323 100644 --- a/packages/ui/src/components/ConfigureSSO/ConfigureSSOSteps.tsx +++ b/packages/ui/src/components/ConfigureSSO/ConfigureSSOSteps.tsx @@ -4,23 +4,35 @@ import { CardStateProvider } from '@/elements/contexts'; import { useConfigureSSO } 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'; /** The ConfigureSSO step graph the entry-guard `` derives its machine from. */ export const ConfigureSSOSteps = (): JSX.Element => { - const { organizationEnterpriseConnection: c } = useConfigureSSO(); + const { organizationEnterpriseConnection: c, organizationDomains } = useConfigureSSO(); + + 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: 'verify-domain', label: 'Verify domains' }, + { + 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], ); // Reset does NOT remount the wizard. Deleting the connection breaks the active @@ -43,7 +55,7 @@ export const ConfigureSSOSteps = (): 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 eb6b7a91cf8..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` -// → `ConfigureSSOSteps` → `` → `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 867c53d5506..1665d8b4b32 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); @@ -148,8 +133,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)); }); @@ -166,7 +150,6 @@ describe('organizationEnterpriseConnection', () => { hasConnection: true, isActive: true, hasMinimumConfiguration: true, - isPrimaryEmailVerified: true, hasSuccessfulTestRun: true, }); }); diff --git a/packages/ui/src/components/ConfigureSSO/domain/organizationEnterpriseConnection.ts b/packages/ui/src/components/ConfigureSSO/domain/organizationEnterpriseConnection.ts index b334f08772d..63f4470c5f7 100644 --- a/packages/ui/src/components/ConfigureSSO/domain/organizationEnterpriseConnection.ts +++ b/packages/ui/src/components/ConfigureSSO/domain/organizationEnterpriseConnection.ts @@ -23,8 +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; - /** The email address whose domain backs the connection. */ - primaryEmail: EmailAddressResource | null | undefined; /** Probed upstream — not a property of the connection resource itself. */ hasSuccessfulTestRun: boolean; } @@ -38,7 +36,6 @@ export interface OrganizationEnterpriseConnection { readonly hasConnection: boolean; readonly isActive: boolean; readonly hasMinimumConfiguration: boolean; - readonly isPrimaryEmailVerified: boolean; readonly hasSuccessfulTestRun: boolean; } @@ -49,13 +46,11 @@ export const isEnterpriseConnectionConfigured = ( export const organizationEnterpriseConnection = ({ connection, - primaryEmail, hasSuccessfulTestRun, }: OrganizationEnterpriseConnectionInput): OrganizationEnterpriseConnection => ({ provider: connection?.provider as ProviderType | undefined, hasConnection: Boolean(connection), isActive: Boolean(connection?.active), hasMinimumConfiguration: isEnterpriseConnectionConfigured(connection), - isPrimaryEmailVerified: primaryEmail?.verification?.status === 'verified', 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..105dd8b8831 100644 --- a/packages/ui/src/components/ConfigureSSO/hooks/__tests__/useOrganizationEnterpriseConnection.test.tsx +++ b/packages/ui/src/components/ConfigureSSO/hooks/__tests__/useOrganizationEnterpriseConnection.test.tsx @@ -176,7 +176,7 @@ describe('useOrganizationEnterpriseConnection — mutations', () => { it('createConnection derives name + domains from the email domain and forwards them (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', emailAddress('admin@acme.com')); expect(mutationSpies.create).toHaveBeenCalledTimes(1); expect(mutationSpies.create).toHaveBeenCalledWith({ @@ -189,14 +189,16 @@ describe('useOrganizationEnterpriseConnection — mutations', () => { it('createConnection resolves to undefined without creating when no email is available', async () => { const { result } = renderHook(() => useOrganizationEnterpriseConnection()); - await expect(result.current.mutations.createConnection('saml_okta', undefined)).resolves.toBeUndefined(); + await expect( + result.current.enterpriseConnectionMutations.createConnection('saml_okta', undefined), + ).resolves.toBeUndefined(); expect(mutationSpies.create).not.toHaveBeenCalled(); }); 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..9270e393227 --- /dev/null +++ b/packages/ui/src/components/ConfigureSSO/steps/OrganizationDomainsStep.tsx @@ -0,0 +1,427 @@ +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 { useCardState } from '@/elements/contexts'; +import { Field } from '@/elements/FieldControl'; +import { Form } from '@/elements/Form'; +import { useClipboard } from '@/hooks'; +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 })}> + + + {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 })} + > + + + + + + + + {/* TODO -> Add height animation when verified */} + {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 })} + > + {/* TODO -> Label name name need to use badge components */} + + + + + {/* TODO -> TXT record value needs to use a readonly input */} + + + ); +}; + +const RecordEntry = ({ + label, + value, + copyable = false, +}: { + label: ReturnType; + value: string; + copyable?: boolean; +}): JSX.Element => { + return ( + ({ gap: t.space.$2, minWidth: 0 })} + > + ({ fontSize: t.fontSizes.$sm, flexShrink: 0 })} + /> + {copyable ? : {value}} + + ); +}; + +const RecordChip = ({ children }: { children: React.ReactNode }): JSX.Element => ( + ({ + borderWidth: t.borderWidths.$normal, + borderStyle: t.borderStyles.$solid, + borderColor: t.colors.$borderAlpha150, + borderRadius: t.radii.$md, + background: t.colors.$neutralAlpha50, + paddingInline: t.space.$1x5, + paddingBlock: t.space.$0x5, + minWidth: 0, + })} + > + ({ + fontFamily: t.fonts.$buttons, + fontSize: t.fontSizes.$xs, + display: 'block', + overflow: 'hidden', + textOverflow: 'ellipsis', + whiteSpace: 'nowrap', + })} + > + {children} + + +); + +const CopyableValue = ({ value }: { value: string }): JSX.Element => { + const { onCopy, hasCopied } = useClipboard(value); + + return ( + ({ gap: t.space.$1, minWidth: 0, flex: 1 })} + > + {value} + + + ); +}; diff --git a/packages/ui/src/components/ConfigureSSO/steps/SelectProviderStep.tsx b/packages/ui/src/components/ConfigureSSO/steps/SelectProviderStep.tsx index b9316321e15..0f74e46f6f1 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(); - // 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); } }; diff --git a/packages/ui/src/components/ConfigureSSO/steps/TestConfigurationStep.tsx b/packages/ui/src/components/ConfigureSSO/steps/TestConfigurationStep.tsx index cda6522f0b7..4aa243a6dd0 100644 --- a/packages/ui/src/components/ConfigureSSO/steps/TestConfigurationStep.tsx +++ b/packages/ui/src/components/ConfigureSSO/steps/TestConfigurationStep.tsx @@ -696,7 +696,7 @@ const OpenTestUrlButton = ({ onTestRunCreated }: OpenTestUrlButtonProps): JSX.El const card = useCardState(); const { enterpriseConnection, - mutations: { createTestRun }, + enterpriseConnectionMutations: { createTestRun }, } = useConfigureSSO(); const [isCreatingTestRun, setIsCreatingTestRun] = useState(false); diff --git a/packages/ui/src/components/ConfigureSSO/steps/VerifyDomainStep.tsx b/packages/ui/src/components/ConfigureSSO/steps/VerifyDomainStep.tsx deleted file mode 100644 index 6ae0e689004..00000000000 --- a/packages/ui/src/components/ConfigureSSO/steps/VerifyDomainStep.tsx +++ /dev/null @@ -1,486 +0,0 @@ -import { useReverification, useUser } from '@clerk/shared/react'; -import type { EmailAddressResource } from '@clerk/shared/types'; -import React, { createContext, useCallback, useContext, useEffect, useRef, useState } from 'react'; - -import { - Col, - descriptors, - Flow, - Heading, - Icon, - Input, - localizationKeys, - Text, - useLocalizations, -} from '@/customizables'; -import { useFieldOTP } from '@/elements/CodeControl'; -import { useCardState } from '@/elements/contexts'; -import { Form } from '@/elements/Form'; -import { DuotoneAtSymbol } from '@/icons'; -import { handleError } from '@/utils/errorHandler'; - -import { connectionBackingEmail } from '../domain/organizationEnterpriseConnection'; -import { Step } from '../elements/Step'; -import { useWizard, Wizard, type WizardStepConfig } from '../elements/Wizard'; -import { InnerStepCounter } from '../elements/Wizard/InnerStepCounter'; - -/** - * The per-instance refs the two inner sub-step bodies share, carried via context - * so the bodies can stay module-scope (stable identity avoids remounting the - * active step on every parent render). - */ -type VerifyDomainRefs = { - emailAddressRef: React.MutableRefObject; - 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..bf0ca368f63 100644 --- a/packages/ui/src/components/ConfigureSSO/steps/__tests__/SelectProviderStep.test.tsx +++ b/packages/ui/src/components/ConfigureSSO/steps/__tests__/SelectProviderStep.test.tsx @@ -17,30 +17,26 @@ vi.mock('../../elements/Wizard', () => ({ 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, }, }), })); @@ -61,8 +57,7 @@ const resetMocks = () => { 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 +161,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 +178,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 +196,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 +210,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 +223,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..5266a922ece 100644 --- a/packages/ui/src/components/OrganizationProfile/EnrollmentBadge.tsx +++ b/packages/ui/src/components/OrganizationProfile/EnrollmentBadge.tsx @@ -7,6 +7,7 @@ const badgeLabelsMap: Record { diff --git a/packages/ui/src/customizables/elementDescriptors.ts b/packages/ui/src/customizables/elementDescriptors.ts index adb4644e66e..45f74acacae 100644 --- a/packages/ui/src/customizables/elementDescriptors.ts +++ b/packages/ui/src/customizables/elementDescriptors.ts @@ -568,6 +568,12 @@ export const APPEARANCE_KEYS = containsAllElementsConfigKeys([ 'configureSSOVerifyDomainErrorIcon', 'configureSSOVerifyDomainErrorTitle', 'configureSSOVerifyDomainErrorSubtitle', + 'configureSSOVerifyDomainList', + '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..14622d1f0b2 100644 --- a/packages/ui/src/internal/appearance.ts +++ b/packages/ui/src/internal/appearance.ts @@ -704,6 +704,12 @@ export type ElementsConfig = { configureSSOVerifyDomainErrorIcon: WithOptions; configureSSOVerifyDomainErrorTitle: WithOptions; configureSSOVerifyDomainErrorSubtitle: WithOptions; + configureSSOVerifyDomainList: WithOptions; + configureSSOVerifyDomainCard: WithOptions<'verified' | 'unverified'>; + configureSSOVerifyDomainCardBadge: WithOptions<'verified' | 'unverified'>; + configureSSOVerifyDomainCardRemoveButton: WithOptions; + configureSSOVerifyDomainCardTxtRecord: WithOptions; + configureSSOVerifyDomainCardTxtRecordValue: WithOptions; configureSSOEmailVerificationForm: WithOptions; configureSSOEmailVerificationIcon: WithOptions; configureSSOEmailVerificationTitle: WithOptions;