// // Copyright 2026 Novaloop AG // // SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. // Please see LICENSE files in the repository root for full details. // import Compound import SwiftUI /// The HealthChat profile fields shown below the header on the profile screens. /// Renders nothing while loading and for profiles without any HealthChat fields. struct HealthChatExtendedProfileSection: View { @State private var loader: HealthChatExtendedProfileLoader init(userID: String, service: ExtendedProfileServiceProtocol) { _loader = State(initialValue: HealthChatExtendedProfileLoader(userID: userID, service: service)) } init(loader: HealthChatExtendedProfileLoader) { _loader = State(initialValue: loader) } var body: some View { if let profile = loader.profile { Section { if let title = profile.title { row(label: HealthChatL10n.commonAcademicTitle, value: title) } if let specialty = profile.specialty { row(label: HealthChatL10n.commonSpecialty, value: specialty) } if let practiceName = profile.practiceName { row(label: HealthChatL10n.commonPracticeName, value: practiceName) } if let address = profile.formattedAddress { row(label: HealthChatL10n.commonAddress, value: address) } if let businessEmail = profile.businessEmail { linkRow(label: HealthChatL10n.commonBusinessEmail, value: businessEmail, url: URL(string: "mailto:\(businessEmail)")) } if let businessTel = profile.businessTel { linkRow(label: HealthChatL10n.commonBusinessTel, value: businessTel, url: URL(string: "tel:\(businessTel.filter { !$0.isWhitespace })")) } if let website = profile.website { linkRow(label: HealthChatL10n.commonWebsite, value: website, url: websiteURL(for: website)) } if let furtherInfo = profile.furtherInfo { row(label: HealthChatL10n.commonFurtherInfo, value: furtherInfo) } } } } // MARK: - Private private func row(label: String, value: String) -> some View { ListRow(kind: .custom { rowContent(label: label, value: value, valueColor: .compound.textPrimary) }) } private func linkRow(label: String, value: String, url: URL?) -> some View { ListRow(kind: .custom { Button { guard let url else { return } UIApplication.shared.open(url) } label: { rowContent(label: label, value: value, valueColor: .compound.textLinkExternal) } }) } private func rowContent(label: String, value: String, valueColor: Color) -> some View { VStack(alignment: .leading, spacing: 2) { Text(label) .font(.compound.bodySM) .foregroundColor(.compound.textSecondary) Text(value) .font(.compound.bodyLG) .foregroundColor(valueColor) } .frame(maxWidth: .infinity, alignment: .leading) .padding(.horizontal, ListRowPadding.horizontal) .padding(.vertical, 8) } private func websiteURL(for website: String) -> URL? { guard let url = URL(string: website) else { return nil } return url.scheme == nil ? URL(string: "https://\(website)") : url } } // MARK: - Previews struct HealthChatExtendedProfileSection_Previews: PreviewProvider, TestablePreview { static let fullProfile = try? JSONDecoder().decode(ExtendedProfile.self, from: Data(""" { "displayname": "Dr. Alice Alpha", "io.element.profile.title": "Dr. med.", "io.element.profile.specialization": "Physiotherapie", "io.element.profile.practice_name": "Praxis Alpha", "io.element.profile.business_street": "Friedaustrasse", "io.element.profile.business_street_nr": "17", "io.element.profile.business_plz": "8003", "io.element.profile.business_city": "Zürich", "io.element.profile.business_email": "praxis@example.ch", "io.element.profile.business_tel": "+41 44 123 45 67", "io.element.profile.website": "https://example.ch", "io.element.profile.further_info": "Krankenkassen-anerkannt\\nHausbesuche möglich" } """.utf8)) static let contactOnlyProfile = try? JSONDecoder().decode(ExtendedProfile.self, from: Data(""" { "io.element.profile.business_tel": "+41 44 123 45 67" } """.utf8)) static var previews: some View { Form { HealthChatExtendedProfileSection(loader: .init(profile: fullProfile)) } .compoundList() .previewDisplayName("Full profile") Form { HealthChatExtendedProfileSection(loader: .init(profile: contactOnlyProfile)) } .compoundList() .previewDisplayName("Contact only") } }