import SwiftUI struct FinishWalkSheet: View { let points: [GPSPoint] let durationSeconds: Int let distanceMeters: Double let onDiscard: () -> Void let onSaved: () -> Void @Environment(\.dismiss) private var dismiss @State private var name: String @State private var selectedDogIds: Set = [] @State private var dogs: [Dog] = [] @State private var isLoadingDogs = false @State private var isSaving = false @State private var errorMessage: String? init( points: [GPSPoint], durationSeconds: Int, distanceMeters: Double, onDiscard: @escaping () -> Void, onSaved: @escaping () -> Void ) { self.points = points self.durationSeconds = durationSeconds self.distanceMeters = distanceMeters self.onDiscard = onDiscard self.onSaved = onSaved let formatter = DateFormatter() formatter.locale = Locale(identifier: "de_DE") formatter.dateStyle = .medium _name = State(initialValue: "Gassi am \(formatter.string(from: .now))") } var body: some View { NavigationStack { Form { Section("Stats") { LabeledContent("Distanz", value: String(format: "%.2f km", distanceMeters / 1000)) LabeledContent("Dauer", value: durationLabel) LabeledContent("Punkte", value: "\(points.count)") } Section("Name") { TextField("Name der Tour", text: $name) } Section("Hunde") { if isLoadingDogs && dogs.isEmpty { HStack { ProgressView(); Text("Lade Hunde…") } } else if dogs.isEmpty { Text("Keine Hunde gefunden. Wird gespeichert ohne Hund-Zuordnung.") .font(.footnote) .foregroundStyle(.secondary) } else { ForEach(dogs) { dog in dogRow(dog) } } } if let errorMessage { Section { Text(errorMessage) .font(.footnote) .foregroundStyle(.red) } } } .navigationTitle("Tour speichern") .navigationBarTitleDisplayMode(.inline) .toolbar { ToolbarItem(placement: .cancellationAction) { Button("Verwerfen", role: .destructive) { onDiscard() dismiss() } } ToolbarItem(placement: .confirmationAction) { if isSaving { ProgressView() } else { Button("Speichern") { Task { await save() } } .disabled(canSave == false) } } } .task { await loadDogs() } .interactiveDismissDisabled(isSaving) } } private func dogRow(_ dog: Dog) -> some View { let selected = selectedDogIds.contains(dog.id) return HStack { Image(systemName: selected ? "checkmark.circle.fill" : "circle") .foregroundStyle(selected ? Color.accentColor : .secondary) VStack(alignment: .leading) { Text(dog.name) if let rasse = dog.rasse, !rasse.isEmpty { Text(rasse).font(.caption).foregroundStyle(.secondary) } } Spacer() } .contentShape(Rectangle()) .onTapGesture { if selected { selectedDogIds.remove(dog.id) } else { selectedDogIds.insert(dog.id) } } } private var durationLabel: String { let mins = durationSeconds / 60 let secs = durationSeconds % 60 if mins >= 60 { return "\(mins / 60) h \(mins % 60) min" } return "\(mins) min \(secs) s" } private var canSave: Bool { !name.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty && points.count >= 2 && !isSaving } private func loadDogs() async { isLoadingDogs = true defer { isLoadingDogs = false } do { let fetched: [Dog] = try await APIClient.shared.get("/api/dogs") self.dogs = fetched // If there's exactly one dog, pre-select it — saves a tap. if fetched.count == 1 { selectedDogIds = [fetched[0].id] } } catch { // Fall back gracefully — user can still save without dogs. print("FinishWalkSheet loadDogs failed: \(error)") } } private func save() async { isSaving = true errorMessage = nil defer { isSaving = false } let body = RouteCreateBody( name: name.trimmingCharacters(in: .whitespacesAndNewlines), gpsTrack: points, distanzKm: distanceMeters / 1000, dauerMin: max(1, durationSeconds / 60), dogIds: Array(selectedDogIds), isPublic: false ) do { let _: RouteDetail = try await APIClient.shared.post("/api/routes", body: body) onSaved() dismiss() } catch { errorMessage = error.localizedDescription } } }