import SwiftUI struct SessionView: View { @EnvironmentObject private var analytics: HermesAnalyticsClient @EnvironmentObject private var localization: LocalizationStore @EnvironmentObject private var repository: HermesRepository let onRetry: () -> Void var body: some View { let session = repository.currentSession let bannerMessage = hermesUserFacingErrorMessage( localization: localization, localeCode: localization.localeCode, error: repository.errorCause ) let statusText: String if session != nil { statusText = localization.string(for: "session.status_ready") } else if bannerMessage != nil { statusText = localization.string(for: "session.status_error") } else { statusText = localization.string(for: "session.status_loading") } return VStack(alignment: .leading, spacing: HermesTheme.sectionSpacing) { HermesSectionHeader( title: localization.string(for: "session.title"), subtitle: localization.string(for: "session.subtitle") ) if session == nil { if let bannerMessage { sessionErrorState( message: bannerMessage, retryText: localization.string(for: "common.retry"), onRetry: onRetry ) } else { sessionLoadingState( title: statusText, subtitle: localization.string(for: "session.note") ) } } else { sessionStatusBadge(text: statusText, warning: bannerMessage != nil) if let bannerMessage { sessionBanner(message: bannerMessage) } sessionRow(label: localization.string(for: "session.id_label"), value: session?.sessionId.uuidString ?? "--") sessionRow(label: localization.string(for: "session.user_id_label"), value: session?.userId.uuidString ?? "--") sessionRow( label: localization.string(for: "session.locale_label"), value: localization.localeName(for: session?.localeCode ?? localization.localeCode) ) sessionRow(label: localization.string(for: "session.started_label"), value: session.map { Self.compactDateFormatter.string(from: $0.startedAt) } ?? "--") sessionRow(label: localization.string(for: "session.variant_label"), value: session?.experimentVariant ?? "--") sessionRow(label: localization.string(for: "session.app_version_label"), value: session?.appVersion ?? "--") sessionRow(label: localization.string(for: "session.device_model_label"), value: session?.deviceModel ?? "--") sessionRow(label: localization.string(for: "session.os_version_label"), value: session?.osVersion ?? "--") Text(localization.string(for: "session.note")) .font(.callout) .foregroundStyle(HermesTheme.textSecondary) } } .hermesCard() .onAppear { analytics.track("screen_viewed", attributes: ["screen_name": "session"]) } } @ViewBuilder private func sessionLoadingState(title: String, subtitle: String) -> some View { VStack(alignment: .leading, spacing: 8) { Text(title) .font(.headline.weight(.semibold)) .foregroundStyle(HermesTheme.textPrimary) Text(subtitle) .font(.callout) .foregroundStyle(HermesTheme.textSecondary) } } @ViewBuilder private func sessionErrorState(message: String, retryText: String, onRetry: @escaping () -> Void) -> some View { VStack(alignment: .leading, spacing: 12) { Text(message) .font(.callout) .foregroundStyle(HermesTheme.warning) Button { onRetry() } label: { Text(retryText) } .buttonStyle(HermesSecondaryButtonStyle()) } } @ViewBuilder private func sessionStatusBadge(text: String, warning: Bool) -> some View { Text(text) .font(.caption.weight(.semibold)) .foregroundStyle(warning ? HermesTheme.warning : HermesTheme.accent) .padding(.horizontal, 12) .padding(.vertical, 8) .background((warning ? HermesTheme.warning : HermesTheme.accent).opacity(0.16)) .clipShape(Capsule()) } @ViewBuilder private func sessionBanner(message: String) -> some View { Text(message) .font(.callout) .foregroundStyle(HermesTheme.warning) .padding(.horizontal, 12) .padding(.vertical, 10) .frame(maxWidth: .infinity, alignment: .leading) .background(HermesTheme.warning.opacity(0.12)) .clipShape(RoundedRectangle(cornerRadius: HermesTheme.insetRadius, style: .continuous)) } @ViewBuilder private func sessionRow(label: String, value: String) -> some View { HStack { Text(label) .font(.callout) .foregroundStyle(HermesTheme.textSecondary) Spacer(minLength: 12) Text(value) .font(.callout.weight(.semibold)) .foregroundStyle(HermesTheme.textPrimary) } } private static let compactDateFormatter: DateFormatter = { let formatter = DateFormatter() formatter.locale = Locale(identifier: "en_US_POSIX") formatter.timeZone = .current formatter.dateFormat = "yyyy-MM-dd HH:mm" return formatter }() }