diff --git a/platform/app/src/routes/Register/Register.tsx b/platform/app/src/routes/Register/Register.tsx index 7c0bb4a42..a75ee0556 100644 --- a/platform/app/src/routes/Register/Register.tsx +++ b/platform/app/src/routes/Register/Register.tsx @@ -324,7 +324,8 @@ const RegisterPage = () => { /> { expect(request.tenantId).toBe('tenant-from-session'); expect(request).not.toHaveProperty('code'); }); + + it('normalizes a blank license number for the public registration API', () => { + const request = createRegistrationRequest({ + tenantId: 'tenant-from-session', + firstName: 'Grace', + lastName: 'Hopper', + email: 'grace@example.com', + password: 'Strong!Password', + licenseNo: ' ', + specialty: 'radiology', + turnstileToken: 'turnstile-proof', + policyAcceptances: [ + { policyKey: 'TERMS_OF_SERVICE', version: 'terms-v1' }, + { policyKey: 'PRIVACY_POLICY', version: 'privacy-v1' }, + ], + }); + + expect(request.licenseNo).toBe(''); + }); }); diff --git a/platform/app/src/routes/Register/registrationValidation.test.ts b/platform/app/src/routes/Register/registrationValidation.test.ts index b91eb4617..81a55ef8d 100644 --- a/platform/app/src/routes/Register/registrationValidation.test.ts +++ b/platform/app/src/routes/Register/registrationValidation.test.ts @@ -20,6 +20,11 @@ describe('getRegistrationValidationMessage', () => { expect(getRegistrationValidationMessage(validRegistration)).toBeNull(); }); + it('accepts an omitted public-registration license number', () => { + expect(getRegistrationValidationMessage({ ...validRegistration, licenseNo: '' })).toBeNull(); + expect(getRegistrationValidationMessage({ ...validRegistration, licenseNo: ' ' })).toBeNull(); + }); + it('rejects invalid email and mismatched passwords clearly', () => { expect(getRegistrationValidationMessage({ ...validRegistration, email: 'not-an-email' })).toBe( 'Please enter a valid email address.' @@ -81,6 +86,12 @@ describe('getRegistrationValidationMessage', () => { lastName: 'y', }) ).toBe('First and last name together must be 100 characters or fewer.'); + expect( + getRegistrationValidationMessage({ + ...validRegistration, + licenseNo: 'x'.repeat(101), + }) + ).toBe('License number must be 100 characters or fewer.'); expect( getRegistrationValidationMessage({ ...validRegistration, diff --git a/platform/app/src/routes/Register/registrationValidation.ts b/platform/app/src/routes/Register/registrationValidation.ts index 5e90097d1..21bde5dbe 100644 --- a/platform/app/src/routes/Register/registrationValidation.ts +++ b/platform/app/src/routes/Register/registrationValidation.ts @@ -53,7 +53,6 @@ export const getRegistrationValidationMessage = ({ !email.trim() || !firstName.trim() || !lastName.trim() || - !licenseNo.trim() || !specialty.trim() || !password || !confirmPassword diff --git a/platform/app/src/routes/Settings/Settings.tsx b/platform/app/src/routes/Settings/Settings.tsx index 0a365bf7b..8a95dec5a 100644 --- a/platform/app/src/routes/Settings/Settings.tsx +++ b/platform/app/src/routes/Settings/Settings.tsx @@ -19,6 +19,7 @@ import tenantRepository from '../../api/tenantRepository'; import { logoutUser } from '../../service/userService'; import { resetTutorialAndNotify } from '../../service/tutorialService'; import { FrontendVersionContext } from '../../App'; +import { getProfileCredentialDetails } from './profileCredentialDetails'; const SettingsPage = () => { const { t } = useTranslation('Settings'); @@ -214,6 +215,11 @@ const SettingsPage = () => { }; const GeneralSettings = () => { + const profileCredentials = getProfileCredentialDetails( + currentUser.specialty, + currentUser.licenseNo + ); + return (
@@ -230,11 +236,13 @@ const SettingsPage = () => {

{currentUser.name}

-
{currentUser.specialty} •
-
- {currentUser.licenseNo} - -
+
{profileCredentials.specialtyLabel}
+ {profileCredentials.licenseNo && ( +
+ {profileCredentials.licenseNo} + +
+ )}
) : ( diff --git a/platform/app/src/routes/Settings/profileCredentialDetails.test.ts b/platform/app/src/routes/Settings/profileCredentialDetails.test.ts new file mode 100644 index 000000000..4a902d48f --- /dev/null +++ b/platform/app/src/routes/Settings/profileCredentialDetails.test.ts @@ -0,0 +1,20 @@ +import { getProfileCredentialDetails } from './profileCredentialDetails'; + +describe('getProfileCredentialDetails', () => { + it('shows a normalized license number after the specialty', () => { + expect(getProfileCredentialDetails('Cardiology', ' MD-123 ')).toEqual({ + specialtyLabel: 'Cardiology •', + licenseNo: 'MD-123', + }); + }); + + it.each([undefined, null, '', ' '])( + 'omits an absent license number and its separator for %p', + licenseNo => { + expect(getProfileCredentialDetails('Cardiology', licenseNo)).toEqual({ + specialtyLabel: 'Cardiology', + licenseNo: null, + }); + } + ); +}); diff --git a/platform/app/src/routes/Settings/profileCredentialDetails.ts b/platform/app/src/routes/Settings/profileCredentialDetails.ts new file mode 100644 index 000000000..3fa252516 --- /dev/null +++ b/platform/app/src/routes/Settings/profileCredentialDetails.ts @@ -0,0 +1,16 @@ +interface ProfileCredentialDetails { + specialtyLabel: string; + licenseNo: string | null; +} + +export const getProfileCredentialDetails = ( + specialty?: string, + licenseNo?: string | null +): ProfileCredentialDetails => { + const normalizedLicenseNo = licenseNo?.trim() || null; + + return { + specialtyLabel: `${specialty || ''}${normalizedLicenseNo ? ' •' : ''}`, + licenseNo: normalizedLicenseNo, + }; +}; diff --git a/platform/i18n/src/locales/ar/Members.json b/platform/i18n/src/locales/ar/Members.json index c9cde1471..d91d007f2 100644 --- a/platform/i18n/src/locales/ar/Members.json +++ b/platform/i18n/src/locales/ar/Members.json @@ -25,6 +25,7 @@ "Invite Sent": "تم إرسال الدعوة", "Last Name": "الاسم الأخير", "License No.": "رقم الترخيص", + "License No. (optional)": "رقم الترخيص (اختياري)", "Name": "الاسم", "Next": "التالي", "No Data Found": "لم يتم العثور على بيانات", diff --git a/platform/i18n/src/locales/de/Members.json b/platform/i18n/src/locales/de/Members.json index 9f1ea5731..d3deb0b5a 100644 --- a/platform/i18n/src/locales/de/Members.json +++ b/platform/i18n/src/locales/de/Members.json @@ -25,6 +25,7 @@ "Invite Sent": "Einladung gesendet", "Last Name": "Nachname", "License No.": "Lizenznummer", + "License No. (optional)": "Lizenznummer (optional)", "Name": "Name", "Next": "Weiter", "No Data Found": "Keine Daten gefunden", diff --git a/platform/i18n/src/locales/en-US/Members.json b/platform/i18n/src/locales/en-US/Members.json index cdb205913..132f9a4c4 100644 --- a/platform/i18n/src/locales/en-US/Members.json +++ b/platform/i18n/src/locales/en-US/Members.json @@ -25,6 +25,7 @@ "Invite Sent": "Invite Sent", "Last Name": "Last Name", "License No.": "License No.", + "License No. (optional)": "License No. (optional)", "Name": "Name", "Next": "Next", "No Data Found": "No Data Found", diff --git a/platform/i18n/src/locales/es/Members.json b/platform/i18n/src/locales/es/Members.json index c07cc7cf5..8fedc273d 100644 --- a/platform/i18n/src/locales/es/Members.json +++ b/platform/i18n/src/locales/es/Members.json @@ -25,6 +25,7 @@ "Invite Sent": "Invitación enviada", "Last Name": "Apellido", "License No.": "Número de Licencia", + "License No. (optional)": "Número de Licencia (opcional)", "Name": "Nombre", "Next": "Siguiente", "No Data Found": "No se Encontraron Datos", diff --git a/platform/i18n/src/locales/fr/Members.json b/platform/i18n/src/locales/fr/Members.json index ef65fe823..d867fbf9e 100644 --- a/platform/i18n/src/locales/fr/Members.json +++ b/platform/i18n/src/locales/fr/Members.json @@ -25,6 +25,7 @@ "Invite Sent": "Invitation envoyée", "Last Name": "Nom", "License No.": "Numéro de licence", + "License No. (optional)": "Numéro de licence (facultatif)", "Name": "Nom", "No Data Found": "Aucune donnée trouvée", "No invitations yet": "Aucune invitation pour le moment.",