import SwiftUI struct ContentView: View { @ObservedObject var viewModel: ImportFlowViewModel var body: some View { NavigationStack { List { NavigationLink { SegmentImportView(viewModel: viewModel) } label: { VStack(alignment: .leading, spacing: 4) { Text(viewModel.template.localizedHealthTypeName) .font(.headline) Text(String(localized: "label.template")) .font(.caption) .foregroundStyle(.secondary) } } } .navigationTitle(String(localized: "screen.title.segments")) } } } private struct SegmentImportView: View { @ObservedObject var viewModel: ImportFlowViewModel @State private var showingFileImporter = false @State private var fieldStates: [String: ImportFlowViewModel.FieldMappingState] = [:] var body: some View { Form { sourceSection fieldsSection testImportSection } .navigationTitle(viewModel.template.localizedHealthTypeName) .navigationBarTitleDisplayMode(.inline) .fileImporter( isPresented: $showingFileImporter, allowedContentTypes: viewModel.supportedContentTypes, allowsMultipleSelection: false ) { result in switch result { case let .success(urls): if let url = urls.first { viewModel.loadFile(from: url) } case let .failure(error): viewModel.errorMessage = error.localizedDescription } } .alert(String(localized: "error.title"), isPresented: Binding( get: { viewModel.errorMessage != nil }, set: { isVisible in if !isVisible { viewModel.errorMessage = nil } } ), actions: { Button(String(localized: "button.ok"), role: .cancel) {} }, message: { Text(viewModel.errorMessage ?? "") }) .onAppear { refreshFieldStates() } .onChange(of: viewModel.entries.count) { _, _ in refreshFieldStates() } .onChange(of: viewModel.mapping) { _, _ in refreshFieldStates() } .onChange(of: viewModel.fieldFormatters) { _, _ in refreshFieldStates() } .onChange(of: viewModel.moodValenceScale) { _, _ in refreshFieldStates() } } private var sourceSection: some View { Section(String(localized: "section.source")) { Button(String(localized: "button.load_json")) { showingFileImporter = true } Button(String(localized: "button.load_sample")) { viewModel.loadBundledSample() } Text(String.localizedStringWithFormat( NSLocalizedString("label.records_loaded", comment: ""), viewModel.entries.count )) if !viewModel.statusMessage.isEmpty { Text(viewModel.statusMessage) .font(.caption) .foregroundStyle(.secondary) } } } private var fieldsSection: some View { Section(String(localized: "section.mapping")) { ForEach(viewModel.template.fields) { field in NavigationLink { FieldMappingDetailView(viewModel: viewModel, field: field) } label: { HStack { VStack(alignment: .leading, spacing: 4) { Text(field.title) Text(field.targetFormat) .font(.caption2) .foregroundStyle(.secondary) } Spacer() Text(fieldStateText(fieldStates[field.id] ?? .incomplete)) .font(.caption2) .foregroundStyle(color(for: fieldStates[field.id] ?? .incomplete)) } } } } } private var testImportSection: some View { Section(String(localized: "section.commit")) { Button(String(localized: "button.run_dry_run")) { viewModel.runDryRun() } if let result = viewModel.dryRunResult { Text(String.localizedStringWithFormat( NSLocalizedString("label.preview_count", comment: ""), result.drafts.count )) if !result.errors.isEmpty { Text(String.localizedStringWithFormat( NSLocalizedString("label.errors_count", comment: ""), result.errors.count )) .foregroundStyle(.red) } if !result.warnings.isEmpty { Text(String.localizedStringWithFormat( NSLocalizedString("label.warnings_count", comment: ""), result.warnings.count )) .foregroundStyle(.orange) } } Button { Task { await viewModel.commitImport() } } label: { if viewModel.isImporting { ProgressView() } else { Text(String(localized: "button.commit")) } } .disabled(viewModel.isImporting) Text(String(localized: "text.commit_notice")) .font(.caption) .foregroundStyle(.secondary) } } private func color(for state: ImportFlowViewModel.FieldMappingState) -> Color { switch state { case .incomplete: return .secondary case .partiallyValid: return .yellow case .valid: return .green } } private func fieldStateText(_ state: ImportFlowViewModel.FieldMappingState) -> String { switch state { case .incomplete: return String(localized: "state.incomplete") case .partiallyValid: return String(localized: "state.partial") case .valid: return String(localized: "state.valid") } } private func refreshFieldStates() { var next: [String: ImportFlowViewModel.FieldMappingState] = [:] for field in viewModel.template.fields { next[field.id] = viewModel.fieldState(for: field) } fieldStates = next } }