From d038c0aafc3516a38da620be93d87712ef15da4a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=ABl=20Poyet?= Date: Fri, 11 Apr 2025 20:32:22 +0200 Subject: [PATCH] feat: improve profile view with additional fields and validation --- .../settings/_ExtendedProfileSettings.pcss | 31 ++ .../views/right_panel/ExtendedProfileView.tsx | 4 - .../right_panel/UserExtendedProfileInfo.tsx | 85 ++++- .../settings/ExtendedProfileSettings.tsx | 300 ++++++++++++++++-- src/i18n/strings/de_DE.json | 15 +- src/i18n/strings/en_EN.json | 15 +- src/i18n/strings/fr.json | 15 +- 7 files changed, 412 insertions(+), 53 deletions(-) diff --git a/res/css/views/settings/_ExtendedProfileSettings.pcss b/res/css/views/settings/_ExtendedProfileSettings.pcss index 8786103941..c0080e5901 100644 --- a/res/css/views/settings/_ExtendedProfileSettings.pcss +++ b/res/css/views/settings/_ExtendedProfileSettings.pcss @@ -32,6 +32,37 @@ } } +/* Business contact field specific layouts */ +.mx_ExtendedProfileSettings_address_row { + .mx_ExtendedProfileSettings_field_business_street { + flex: 1; + } + + .mx_ExtendedProfileSettings_field_business_street_nr { + flex: 1; + } +} + +.mx_ExtendedProfileSettings_postal_row { + .mx_ExtendedProfileSettings_field_business_plz { + flex: 1; + } + + .mx_ExtendedProfileSettings_field_business_city { + flex: 1; + } +} + +.mx_ExtendedProfileSettings_contact_row { + .mx_ExtendedProfileSettings_field_business_email { + flex: 1; + } + + .mx_ExtendedProfileSettings_field_business_tel { + flex: 1; + } +} + .mx_ExtendedProfileSettings_buttons { display: flex; gap: 8px; diff --git a/src/components/views/right_panel/ExtendedProfileView.tsx b/src/components/views/right_panel/ExtendedProfileView.tsx index 1f351c6f84..4ef4e9ccf0 100644 --- a/src/components/views/right_panel/ExtendedProfileView.tsx +++ b/src/components/views/right_panel/ExtendedProfileView.tsx @@ -18,7 +18,6 @@ import { SdkContextClass } from "../../../contexts/SDKContext"; import { Container } from "../right_panel/UserInfo"; import InlineSpinner from "../elements/InlineSpinner"; import { - EXT_PROFILE_KEY_ADDRESS, EXT_PROFILE_KEY_FURTHER_INFO, EXT_PROFILE_KEY_SPECIALIZATION, EXT_PROFILE_KEY_TITLE, @@ -61,13 +60,11 @@ const ExtendedProfileView: React.FC = ({ userId }) => const [ titleValue, specializationValue, - addressValue, websiteValue, furtherInfoValue, ] = await Promise.all([ userProfilesStore.getOrFetchExtendedProfileProperty(userId, EXT_PROFILE_KEY_TITLE), userProfilesStore.getOrFetchExtendedProfileProperty(userId, EXT_PROFILE_KEY_SPECIALIZATION), - userProfilesStore.getOrFetchExtendedProfileProperty(userId, EXT_PROFILE_KEY_ADDRESS), userProfilesStore.getOrFetchExtendedProfileProperty(userId, EXT_PROFILE_KEY_WEBSITE), userProfilesStore.getOrFetchExtendedProfileProperty(userId, EXT_PROFILE_KEY_FURTHER_INFO), ]); @@ -75,7 +72,6 @@ const ExtendedProfileView: React.FC = ({ userId }) => const data = { title: titleValue as string | null, specialization: specializationValue as string | null, - address: addressValue as string | null, website: websiteValue as string | null, furtherInfo: furtherInfoValue as string | null, }; diff --git a/src/components/views/right_panel/UserExtendedProfileInfo.tsx b/src/components/views/right_panel/UserExtendedProfileInfo.tsx index 905a1cc32a..484e7c7941 100644 --- a/src/components/views/right_panel/UserExtendedProfileInfo.tsx +++ b/src/components/views/right_panel/UserExtendedProfileInfo.tsx @@ -15,7 +15,13 @@ import { _t } from "../../../languageHandler"; import { EXT_PROFILE_KEY_TITLE, EXT_PROFILE_KEY_SPECIALIZATION, - EXT_PROFILE_KEY_ADDRESS, + EXT_PROFILE_KEY_PRACTICE_NAME, + EXT_PROFILE_KEY_BUSINESS_STREET, + EXT_PROFILE_KEY_BUSINESS_STREET_NR, + EXT_PROFILE_KEY_BUSINESS_CITY, + EXT_PROFILE_KEY_BUSINESS_PLZ, + EXT_PROFILE_KEY_BUSINESS_EMAIL, + EXT_PROFILE_KEY_BUSINESS_TEL, EXT_PROFILE_KEY_WEBSITE, EXT_PROFILE_KEY_FURTHER_INFO, } from "../settings/ExtendedProfileSettings"; @@ -41,7 +47,13 @@ const UserExtendedProfileInfo: React.FC = ({ userI const [title, setTitle] = useState(""); const [specialization, setSpecialization] = useState(""); - const [address, setAddress] = useState(""); + const [practiceName, setPracticeName] = useState(""); + const [businessStreet, setBusinessStreet] = useState(""); + const [businessStreetNr, setBusinessStreetNr] = useState(""); + const [businessCity, setBusinessCity] = useState(""); + const [businessPlz, setBusinessPlz] = useState(""); + const [businessEmail, setBusinessEmail] = useState(""); + const [businessTel, setBusinessTel] = useState(""); const [website, setWebsite] = useState(""); const [furtherInfo, setFurtherInfo] = useState(""); const [loading, setLoading] = useState(true); @@ -55,13 +67,25 @@ const UserExtendedProfileInfo: React.FC = ({ userI const titleValue = await userProfilesStore.getOrFetchExtendedProfileProperty(userId, EXT_PROFILE_KEY_TITLE); const specializationValue = await userProfilesStore.getOrFetchExtendedProfileProperty(userId, EXT_PROFILE_KEY_SPECIALIZATION); - const addressValue = await userProfilesStore.getOrFetchExtendedProfileProperty(userId, EXT_PROFILE_KEY_ADDRESS); + const practiceNameValue = await userProfilesStore.getOrFetchExtendedProfileProperty(userId, EXT_PROFILE_KEY_PRACTICE_NAME); + const businessStreetValue = await userProfilesStore.getOrFetchExtendedProfileProperty(userId, EXT_PROFILE_KEY_BUSINESS_STREET); + const businessStreetNrValue = await userProfilesStore.getOrFetchExtendedProfileProperty(userId, EXT_PROFILE_KEY_BUSINESS_STREET_NR); + const businessCityValue = await userProfilesStore.getOrFetchExtendedProfileProperty(userId, EXT_PROFILE_KEY_BUSINESS_CITY); + const businessPlzValue = await userProfilesStore.getOrFetchExtendedProfileProperty(userId, EXT_PROFILE_KEY_BUSINESS_PLZ); + const businessEmailValue = await userProfilesStore.getOrFetchExtendedProfileProperty(userId, EXT_PROFILE_KEY_BUSINESS_EMAIL); + const businessTelValue = await userProfilesStore.getOrFetchExtendedProfileProperty(userId, EXT_PROFILE_KEY_BUSINESS_TEL); const websiteValue = await userProfilesStore.getOrFetchExtendedProfileProperty(userId, EXT_PROFILE_KEY_WEBSITE); const furtherInfoValue = await userProfilesStore.getOrFetchExtendedProfileProperty(userId, EXT_PROFILE_KEY_FURTHER_INFO); setTitle(titleValue as string || ""); setSpecialization(specializationValue as string || ""); - setAddress(addressValue as string || ""); + setPracticeName(practiceNameValue as string || ""); + setBusinessStreet(businessStreetValue as string || ""); + setBusinessStreetNr(businessStreetNrValue as string || ""); + setBusinessCity(businessCityValue as string || ""); + setBusinessPlz(businessPlzValue as string || ""); + setBusinessEmail(businessEmailValue as string || ""); + setBusinessTel(businessTelValue as string || ""); setWebsite(websiteValue as string || ""); setFurtherInfo(furtherInfoValue as string || ""); } catch { @@ -83,15 +107,12 @@ const UserExtendedProfileInfo: React.FC = ({ userI } // Don't render anything if there's no data - if (!title && !specialization && !address && !website && !furtherInfo) { + if (!title && !specialization && !practiceName && !businessStreet && !businessStreetNr && !businessCity && !businessPlz && !businessEmail && !businessTel && !website && !furtherInfo) { return null; } return (
-{/*
- {_t("user_info|extended_profile|header")} -
*/} {title && (
@@ -116,7 +137,19 @@ const UserExtendedProfileInfo: React.FC = ({ userI
)} - {address && ( + {practiceName && ( +
+
+ + {_t("user_info|extended_profile|practice_name")} + +
+
+ {practiceName} +
+
+ )} + {businessStreet && businessCity && (
@@ -124,7 +157,39 @@ const UserExtendedProfileInfo: React.FC = ({ userI
- {renderMultilineText(address)} + + {[businessStreet, businessStreetNr].filter(Boolean).join(" ")} + {(businessStreet || businessStreetNr) && (businessPlz || businessCity) && ", "} + {[businessPlz, businessCity].filter(Boolean).join(" ")} + +
+
+ )} + {businessEmail && ( +
+
+ + {_t("user_info|extended_profile|business_email")} + +
+
+ + {businessEmail} + +
+
+ )} + {businessTel && ( +
+
+ + {_t("user_info|extended_profile|business_tel")} + +
+
)} diff --git a/src/components/views/settings/ExtendedProfileSettings.tsx b/src/components/views/settings/ExtendedProfileSettings.tsx index 87ae9235ff..527315ed17 100644 --- a/src/components/views/settings/ExtendedProfileSettings.tsx +++ b/src/components/views/settings/ExtendedProfileSettings.tsx @@ -7,7 +7,7 @@ Please see LICENSE files in the repository root for full details. import React, { type ChangeEvent, useCallback, useEffect, useState } from "react"; import { logger } from "matrix-js-sdk/src/logger"; -import { Alert, ErrorMessage } from "@vector-im/compound-web"; +import { Alert } from "@vector-im/compound-web"; import classNames from "classnames"; import { _t } from "../../../languageHandler"; @@ -28,9 +28,15 @@ const SpinnerToast: React.FC<{ children?: React.ReactNode }> = ({ children }) => // Extended profile property keys export const EXT_PROFILE_KEY_TITLE = "io.element.profile.title"; export const EXT_PROFILE_KEY_SPECIALIZATION = "io.element.profile.specialization"; -export const EXT_PROFILE_KEY_ADDRESS = "io.element.profile.address"; export const EXT_PROFILE_KEY_WEBSITE = "io.element.profile.website"; export const EXT_PROFILE_KEY_FURTHER_INFO = "io.element.profile.further_info"; +export const EXT_PROFILE_KEY_PRACTICE_NAME = "io.element.profile.practice_name"; +export const EXT_PROFILE_KEY_BUSINESS_STREET = "io.element.profile.business_street"; +export const EXT_PROFILE_KEY_BUSINESS_STREET_NR = "io.element.profile.business_street_nr"; +export const EXT_PROFILE_KEY_BUSINESS_CITY = "io.element.profile.business_city"; +export const EXT_PROFILE_KEY_BUSINESS_PLZ = "io.element.profile.business_plz"; +export const EXT_PROFILE_KEY_BUSINESS_EMAIL = "io.element.profile.business_email"; +export const EXT_PROFILE_KEY_BUSINESS_TEL = "io.element.profile.business_tel"; // Unstable feature key for extended profiles export const UNSTABLE_MSC4133_EXTENDED_PROFILES = "org.matrix.msc4133.extended_profiles"; @@ -41,21 +47,41 @@ interface ExtendedProfileSettingsProps { /** * Component for managing extended profile fields for medical professionals. - * Allows editing title, specialization, address, website, and further information. + * Allows editing title, specialization, website, and further information. */ const ExtendedProfileSettings: React.FC = ({ className }) => { const [title, setTitle] = useState(""); const [specialization, setSpecialization] = useState(""); - const [address, setAddress] = useState(""); const [website, setWebsite] = useState(""); const [furtherInfo, setFurtherInfo] = useState(""); + const [practiceName, setPracticeName] = useState(""); + + // Business contact field states + const [businessStreet, setBusinessStreet] = useState(""); + const [businessStreetNr, setBusinessStreetNr] = useState(""); + const [businessCity, setBusinessCity] = useState(""); + const [businessPlz, setBusinessPlz] = useState(""); + const [businessEmail, setBusinessEmail] = useState(""); + const [businessTel, setBusinessTel] = useState(""); + + // Validation states for email and phone + const [businessEmailValid, setBusinessEmailValid] = useState(true); + const [businessTelValid, setBusinessTelValid] = useState(true); // Store original values for cancel functionality const [originalTitle, setOriginalTitle] = useState(""); const [originalSpecialization, setOriginalSpecialization] = useState(""); - const [originalAddress, setOriginalAddress] = useState(""); const [originalWebsite, setOriginalWebsite] = useState(""); const [originalFurtherInfo, setOriginalFurtherInfo] = useState(""); + const [originalPracticeName, setOriginalPracticeName] = useState(""); + + // Original business contact field values + const [originalBusinessStreet, setOriginalBusinessStreet] = useState(""); + const [originalBusinessStreetNr, setOriginalBusinessStreetNr] = useState(""); + const [originalBusinessCity, setOriginalBusinessCity] = useState(""); + const [originalBusinessPlz, setOriginalBusinessPlz] = useState(""); + const [originalBusinessEmail, setOriginalBusinessEmail] = useState(""); + const [originalBusinessTel, setOriginalBusinessTel] = useState(""); const [isModified, setIsModified] = useState(false); const [isSaving, setIsSaving] = useState(false); @@ -76,28 +102,60 @@ const ExtendedProfileSettings: React.FC = ({ class try { const titleValue = await userProfilesStore.getOrFetchExtendedProfileProperty(userId, EXT_PROFILE_KEY_TITLE); const specializationValue = await userProfilesStore.getOrFetchExtendedProfileProperty(userId, EXT_PROFILE_KEY_SPECIALIZATION); - const addressValue = await userProfilesStore.getOrFetchExtendedProfileProperty(userId, EXT_PROFILE_KEY_ADDRESS); const websiteValue = await userProfilesStore.getOrFetchExtendedProfileProperty(userId, EXT_PROFILE_KEY_WEBSITE); const furtherInfoValue = await userProfilesStore.getOrFetchExtendedProfileProperty(userId, EXT_PROFILE_KEY_FURTHER_INFO); + const practiceNameValue = await userProfilesStore.getOrFetchExtendedProfileProperty(userId, EXT_PROFILE_KEY_PRACTICE_NAME); + + // Fetch business contact fields + const businessStreetValue = await userProfilesStore.getOrFetchExtendedProfileProperty(userId, EXT_PROFILE_KEY_BUSINESS_STREET); + const businessStreetNrValue = await userProfilesStore.getOrFetchExtendedProfileProperty(userId, EXT_PROFILE_KEY_BUSINESS_STREET_NR); + const businessCityValue = await userProfilesStore.getOrFetchExtendedProfileProperty(userId, EXT_PROFILE_KEY_BUSINESS_CITY); + const businessPlzValue = await userProfilesStore.getOrFetchExtendedProfileProperty(userId, EXT_PROFILE_KEY_BUSINESS_PLZ); + const businessEmailValue = await userProfilesStore.getOrFetchExtendedProfileProperty(userId, EXT_PROFILE_KEY_BUSINESS_EMAIL); + const businessTelValue = await userProfilesStore.getOrFetchExtendedProfileProperty(userId, EXT_PROFILE_KEY_BUSINESS_TEL); const titleStr = titleValue as string || ""; const specializationStr = specializationValue as string || ""; - const addressStr = addressValue as string || ""; const websiteStr = websiteValue as string || ""; const furtherInfoStr = furtherInfoValue as string || ""; + const practiceNameStr = practiceNameValue as string || ""; + + // Convert business contact field values to strings + const businessStreetStr = businessStreetValue as string || ""; + const businessStreetNrStr = businessStreetNrValue as string || ""; + const businessCityStr = businessCityValue as string || ""; + const businessPlzStr = businessPlzValue as string || ""; + const businessEmailStr = businessEmailValue as string || ""; + const businessTelStr = businessTelValue as string || ""; setTitle(titleStr); setSpecialization(specializationStr); - setAddress(addressStr); setWebsite(websiteStr); setFurtherInfo(furtherInfoStr); + setPracticeName(practiceNameStr); + + // Set business contact fields + setBusinessStreet(businessStreetStr); + setBusinessStreetNr(businessStreetNrStr); + setBusinessCity(businessCityStr); + setBusinessPlz(businessPlzStr); + setBusinessEmail(businessEmailStr); + setBusinessTel(businessTelStr); // Store original values for cancel functionality setOriginalTitle(titleStr); setOriginalSpecialization(specializationStr); - setOriginalAddress(addressStr); setOriginalWebsite(websiteStr); setOriginalFurtherInfo(furtherInfoStr); + setOriginalPracticeName(practiceNameStr); + + // Store original business contact field values + setOriginalBusinessStreet(businessStreetStr); + setOriginalBusinessStreetNr(businessStreetNrStr); + setOriginalBusinessCity(businessCityStr); + setOriginalBusinessPlz(businessPlzStr); + setOriginalBusinessEmail(businessEmailStr); + setOriginalBusinessTel(businessTelStr); } catch (e) { // Just log the error but don't set loadError to true // as we want the fields to show up even when empty @@ -117,13 +175,24 @@ const ExtendedProfileSettings: React.FC = ({ class const hasChanges = title !== originalTitle || specialization !== originalSpecialization || - address !== originalAddress || website !== originalWebsite || - furtherInfo !== originalFurtherInfo; + furtherInfo !== originalFurtherInfo || + practiceName !== originalPracticeName || + businessStreet !== originalBusinessStreet || + businessStreetNr !== originalBusinessStreetNr || + businessCity !== originalBusinessCity || + businessPlz !== originalBusinessPlz || + businessEmail !== originalBusinessEmail || + businessTel !== originalBusinessTel; setIsModified(hasChanges); - }, [title, specialization, address, website, furtherInfo, - originalTitle, originalSpecialization, originalAddress, originalWebsite, originalFurtherInfo]); + }, [ + title, specialization, website, furtherInfo, practiceName, + originalTitle, originalSpecialization, originalWebsite, originalFurtherInfo, originalPracticeName, + businessStreet, businessStreetNr, businessCity, businessPlz, businessEmail, businessTel, + originalBusinessStreet, originalBusinessStreetNr, originalBusinessCity, originalBusinessPlz, + originalBusinessEmail, originalBusinessTel + ]); // Field change handlers const onTitleChanged = useCallback((e: ChangeEvent) => { @@ -134,10 +203,6 @@ const ExtendedProfileSettings: React.FC = ({ class setSpecialization(e.target.value); }, []); - const onAddressChanged = useCallback((e: ChangeEvent) => { - setAddress(e.target.value); - }, []); - const onWebsiteChanged = useCallback((e: ChangeEvent) => { setWebsite(e.target.value); }, []); @@ -146,19 +211,80 @@ const ExtendedProfileSettings: React.FC = ({ class setFurtherInfo(e.target.value); }, []); + const onPracticeNameChanged = useCallback((e: ChangeEvent) => { + setPracticeName(e.target.value); + }, []); + + // Business contact field change handlers + const onBusinessStreetChanged = useCallback((e: ChangeEvent) => { + setBusinessStreet(e.target.value); + }, []); + + const onBusinessStreetNrChanged = useCallback((e: ChangeEvent) => { + setBusinessStreetNr(e.target.value); + }, []); + + const onBusinessCityChanged = useCallback((e: ChangeEvent) => { + setBusinessCity(e.target.value); + }, []); + + const onBusinessPlzChanged = useCallback((e: ChangeEvent) => { + setBusinessPlz(e.target.value); + }, []); + + // Email validation with simple regex + const onBusinessEmailChanged = useCallback((e: ChangeEvent) => { + const value = e.target.value; + setBusinessEmail(value); + // Email format validation - basic check for something@something.something + const emailValid = value === "" || /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value); + setBusinessEmailValid(emailValid); + }, []); + + // Phone validation + const onBusinessTelChanged = useCallback((e: ChangeEvent) => { + const value = e.target.value; + setBusinessTel(value); + // Phone format validation - allow digits, spaces, plus, minus, parentheses + const phoneValid = value === "" || /^[0-9+\-() ]+$/.test(value); + setBusinessTelValid(phoneValid); + }, []); + // Cancel handler - restore all original values const onCancel = useCallback(() => { setTitle(originalTitle); setSpecialization(originalSpecialization); - setAddress(originalAddress); setWebsite(originalWebsite); setFurtherInfo(originalFurtherInfo); - }, [originalTitle, originalSpecialization, originalAddress, originalWebsite, originalFurtherInfo]); + setPracticeName(originalPracticeName); + + // Reset business contact fields to original values + setBusinessStreet(originalBusinessStreet); + setBusinessStreetNr(originalBusinessStreetNr); + setBusinessCity(originalBusinessCity); + setBusinessPlz(originalBusinessPlz); + setBusinessEmail(originalBusinessEmail); + setBusinessTel(originalBusinessTel); + + // Reset validation states + setBusinessEmailValid(true); + setBusinessTelValid(true); + }, [ + originalTitle, originalSpecialization, originalWebsite, originalFurtherInfo, originalPracticeName, + originalBusinessStreet, originalBusinessStreetNr, originalBusinessCity, originalBusinessPlz, + originalBusinessEmail, originalBusinessTel + ]); // Save handler - save all fields at once const onSave = useCallback(async (): Promise => { if (!isModified) return; + // Validate email and phone fields before saving + if (!businessEmailValid || !businessTelValid) { + setSaveError(true); + return; + } + setIsSaving(true); setSaveError(false); const removeToast = toastRack.displayToast( @@ -177,10 +303,6 @@ const ExtendedProfileSettings: React.FC = ({ class savePromises.push(userProfilesStore.setExtendedProfileProperty(EXT_PROFILE_KEY_SPECIALIZATION, specialization)); } - if (address !== originalAddress) { - savePromises.push(userProfilesStore.setExtendedProfileProperty(EXT_PROFILE_KEY_ADDRESS, address)); - } - if (website !== originalWebsite) { savePromises.push(userProfilesStore.setExtendedProfileProperty(EXT_PROFILE_KEY_WEBSITE, website)); } @@ -189,14 +311,51 @@ const ExtendedProfileSettings: React.FC = ({ class savePromises.push(userProfilesStore.setExtendedProfileProperty(EXT_PROFILE_KEY_FURTHER_INFO, furtherInfo)); } + if (practiceName !== originalPracticeName) { + savePromises.push(userProfilesStore.setExtendedProfileProperty(EXT_PROFILE_KEY_PRACTICE_NAME, practiceName)); + } + + // Save business contact fields if modified + if (businessStreet !== originalBusinessStreet) { + savePromises.push(userProfilesStore.setExtendedProfileProperty(EXT_PROFILE_KEY_BUSINESS_STREET, businessStreet)); + } + + if (businessStreetNr !== originalBusinessStreetNr) { + savePromises.push(userProfilesStore.setExtendedProfileProperty(EXT_PROFILE_KEY_BUSINESS_STREET_NR, businessStreetNr)); + } + + if (businessCity !== originalBusinessCity) { + savePromises.push(userProfilesStore.setExtendedProfileProperty(EXT_PROFILE_KEY_BUSINESS_CITY, businessCity)); + } + + if (businessPlz !== originalBusinessPlz) { + savePromises.push(userProfilesStore.setExtendedProfileProperty(EXT_PROFILE_KEY_BUSINESS_PLZ, businessPlz)); + } + + if (businessEmail !== originalBusinessEmail) { + savePromises.push(userProfilesStore.setExtendedProfileProperty(EXT_PROFILE_KEY_BUSINESS_EMAIL, businessEmail)); + } + + if (businessTel !== originalBusinessTel) { + savePromises.push(userProfilesStore.setExtendedProfileProperty(EXT_PROFILE_KEY_BUSINESS_TEL, businessTel)); + } + await Promise.all(savePromises); // Update original values after successful save setOriginalTitle(title); setOriginalSpecialization(specialization); - setOriginalAddress(address); setOriginalWebsite(website); setOriginalFurtherInfo(furtherInfo); + setOriginalPracticeName(practiceName); + + // Update original business contact field values + setOriginalBusinessStreet(businessStreet); + setOriginalBusinessStreetNr(businessStreetNr); + setOriginalBusinessCity(businessCity); + setOriginalBusinessPlz(businessPlz); + setOriginalBusinessEmail(businessEmail); + setOriginalBusinessTel(businessTel); } catch (e) { logger.error("Failed to save profile information", e); @@ -207,8 +366,11 @@ const ExtendedProfileSettings: React.FC = ({ class removeToast(); } }, [ - isModified, title, specialization, address, website, furtherInfo, - originalTitle, originalSpecialization, originalAddress, originalWebsite, originalFurtherInfo, + isModified, title, specialization, website, furtherInfo, practiceName, + originalTitle, originalSpecialization, originalWebsite, originalFurtherInfo, originalPracticeName, + businessStreet, businessStreetNr, businessCity, businessPlz, businessEmail, businessTel, + originalBusinessStreet, originalBusinessStreetNr, originalBusinessCity, originalBusinessPlz, + originalBusinessEmail, originalBusinessTel, businessEmailValid, businessTelValid, toastRack, userProfilesStore ]); @@ -248,14 +410,85 @@ const ExtendedProfileSettings: React.FC = ({ class
+ {/* Street and number in one row, street takes 2/3 width */} +
+
+ +
+
+ +
+
+ + {/* PLZ and City in one row, PLZ takes 1/3 width */} +
+
+ +
+
+ +
+
+ + {/* Email field with validation */} +
+
+ +
+ + {/* Telephone field with validation */} +
+ +
+
+ +
= ({ class
- {saveError && ( -
- {_t("settings|extended_profile_settings|failed_to_save")} -
+ + {businessEmailValid ? null : _t("settings|extended_profile_settings|invalid_email")} + + {businessTelValid ? null : _t("settings|extended_profile_settings|invalid_phone")} + )} diff --git a/src/i18n/strings/de_DE.json b/src/i18n/strings/de_DE.json index efc6fcb3eb..6584a46513 100644 --- a/src/i18n/strings/de_DE.json +++ b/src/i18n/strings/de_DE.json @@ -2562,13 +2562,21 @@ "additional_information": "Zusätzliche Informationen", "professional_title": "Titel", "specialization": "Fachgebiet", - "address": "Adresse", + "practice_name": "Praxisname / Firmenname", + "business_street": "Strasse", + "business_street_nr": "Hausnummer", + "business_plz": "PLZ", + "business_city": "Ort", + "business_email": "Email (geschäftlich)", + "business_tel": "Telefon (geschäftlich)", "website": "Webseite", "further_information": "Weitere Informationen / Qualifikationen", "saving_profile": "Profil wird gespeichert...", "failed_to_load": "Fehler beim Laden der erweiterten Profileinstellungen.", "failed_to_save": "Fehler beim Speichern der erweiterten Profileinstellungen.", - "server_not_supported": "Dieser Server unterstützt keine erweiterten Profileinstellungen." + "server_not_supported": "Dieser Server unterstützt keine erweiterten Profileinstellungen.", + "invalid_email": "Ungültige E-Mail-Adresse.", + "invalid_phone": "Ungültige Telefonnummer." }, "account": { "dialog_title": "Einstellungen: Konto", @@ -3937,7 +3945,10 @@ "header": "Weitere Informationen", "title": "Titel", "specialization": "Fachgebiet", + "practice_name": "Praxisname / Firmenname", "address": "Adresse", + "business_email": "E-Mail (geschäftlich)", + "business_tel": "Telefon (geschäftlich)", "website": "Webseite", "further_info": "Weitere Infos / Qualifikationen", "not_provided": "Nicht angegeben", diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index 08664baa78..a37ee07ab3 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -2580,13 +2580,21 @@ "additional_information": "Additional Information", "professional_title": "Title", "specialization": "Specialization", - "address": "Address", + "practice_name": "Practice Name", + "business_street": "Strasse", + "business_street_nr": "Street Number", + "business_plz": "ZIP Code", + "business_city": "City", + "business_email": "Business Email", + "business_tel": "Business Phone", "website": "Website", "further_information": "Further Information / Qualifications", "saving_profile": "Saving profile...", "failed_to_load": "Failed to load extended profile settings.", "failed_to_save": "Failed to save extended profile settings.", - "server_not_supported": "Your server does not support extended profile settings." + "server_not_supported": "Your server does not support extended profile settings.", + "invalid_email": "Invalid email.", + "invalid_phone": "Invalid phone number." }, "account": { "dialog_title": "Settings: Account", @@ -3956,7 +3964,10 @@ "header": "Additional infos", "title": "Title", "specialization": "Speciality", + "practice_name": "Practice name", "address": "Address", + "business_email": "Business email", + "business_tel": "Business phone", "website": "Website", "further_info": "Further infos / qualifications", "not_provided": "Not provided", diff --git a/src/i18n/strings/fr.json b/src/i18n/strings/fr.json index a288cd2c2a..ce197dd366 100644 --- a/src/i18n/strings/fr.json +++ b/src/i18n/strings/fr.json @@ -2567,13 +2567,21 @@ "additional_information": "Informations supplémentaires", "professional_title": "Titre", "specialization": "Spécialisation", - "address": "Adresse", + "practice_name": "Nom de l’entreprise", + "business_street": "Rue", + "business_street_nr": "Numéro de rue", + "business_plz": "Code postal", + "business_city": "Ville", + "business_email": "E-mail professionnel", + "business_tel": "Téléphone professionnel", "website": "Site web", "further_information": "Informations supplémentaires / qualifications", "saving_profile": "Enregistrement des paramètres de profil étendus…", "failed_to_load": "Erreur lors du chargement des paramètres de profil étendus.", "failed_to_save": "Erreur lors de l'enregistrement des paramètres de profil étendus.", - "server_not_supported": "Le serveur d’accueil ne prend pas en charge les paramètres de profil étendus." + "server_not_supported": "Le serveur d’accueil ne prend pas en charge les paramètres de profil étendus.", + "invalid_email": "Adresse e-mail invalide.", + "invalid_phone": "Numéro de téléphone invalide." }, "account": { "dialog_title": "Paramètres : Compte", @@ -3942,7 +3950,10 @@ "header": "Informations supplémentaires", "title": "Titre", "specialization": "Spécialisation", + "practice_name": "Nom de l’entreprise", "address": "Adresse", + "business_email": "Email (professionnel)", + "business_tel": "Téléphone (professionnel)", "website": "Site web", "further_info": "Informations supplémentaires / qualifications", "not_provided": "Non fourni",