import Foundation // MARK: - Auth struct LoginRequest: Encodable { let email: String let password: String } struct LoginResponse: Decodable { let token: String let name: String let isPremium: Bool } struct UserProfile: Decodable { let id: Int let name: String let email: String let realName: String? let rolle: String? let isPremium: Bool? // Backend bool-konvertiert nur is_premium; is_founder/is_partner kommen // als SQLite-Int 0/1 zurück — deshalb hier Int? statt Bool?. let isFounder: Int? let isPartner: Int? let founderNumber: Int? let subscriptionTier: String? let avatarUrl: String? let wohnort: String? let bio: String? var isFounderFlag: Bool { isFounder == 1 } var isPartnerFlag: Bool { isPartner == 1 } } // MARK: - Dogs struct Dog: Decodable, Identifiable { let id: Int let name: String let rasse: String? let fotoUrl: String? let geburtstag: String? } // MARK: - Routes struct GPSPoint: Codable, Hashable { let lat: Double let lon: Double let alt: Double? } struct RouteListItem: Decodable, Identifiable { let id: Int let userId: Int let name: String let beschreibung: String? let distanzKm: Double? let dauerMin: Int? let createdAt: String? let previewTrack: [GPSPoint] let fotoUrls: [String]? let userName: String? let isPublic: Bool? } struct RouteDetail: Decodable, Identifiable { let id: Int let userId: Int let name: String let beschreibung: String? let distanzKm: Double? let dauerMin: Int? let gpsTrack: [GPSPoint] let fotoUrls: [String]? let createdAt: String? let userName: String? let dogIds: [Int]? } struct RouteCreateBody: Encodable { let name: String let gpsTrack: [GPSPoint] let distanzKm: Double let dauerMin: Int let dogIds: [Int] let isPublic: Bool } // MARK: - Expenses struct Expense: Decodable, Identifiable { let id: Int let dogId: Int? let kategorie: String let betrag: Double let datum: String let notiz: String? let dogName: String? } struct ExpenseCreateBody: Encodable { let dogId: Int? let kategorie: String let betrag: Double let datum: String let notiz: String? } struct ExpenseCategory: Decodable, Identifiable { let id: String let label: String let color: String? } // MARK: - Gassi-Zeiten struct GassiZeit: Decodable, Identifiable { let id: Int let dogId: Int? let wochentage: [String] let uhrzeit: String let ortName: String? let lat: Double? let lon: Double? let radiusM: Int? let notiz: String? let aktiv: Int? let distanceM: Int? let isMine: Bool? let userName: String? let dogName: String? let dogRasse: String? } struct GassiZeitCreateBody: Encodable { let dogId: Int? let wochentage: [String] let uhrzeit: String let ortName: String? let lat: Double? let lon: Double? let radiusM: Int let notiz: String? } // MARK: - Poison struct PoisonAlert: Decodable, Identifiable { let id: Int let lat: Double let lon: Double let beschreibung: String? let typ: String? let distanzM: Int? let fotoUrl: String? let melderName: String? let createdAt: String? } struct PoisonCreateBody: Encodable { let lat: Double let lon: Double let beschreibung: String? let typ: String } // MARK: - Lost Dogs struct LostDog: Decodable, Identifiable { let id: Int let name: String let rasse: String? let beschreibung: String let lat: Double let lon: Double let distanzM: Int? let fotoUrl: String? let melderName: String? let createdAt: String? } struct LostDogCreateBody: Encodable { let name: String let rasse: String? let beschreibung: String let lat: Double let lon: Double let dogId: Int? } // MARK: - Diary (Tagebuch) struct DiaryEntry: Decodable, Identifiable { let id: Int let dogId: Int? let datum: String? let typ: String? let titel: String? let text: String? let tags: [String]? let gpsLat: Double? let gpsLon: Double? let locationName: String? let isMilestone: Bool? let media: [DiaryMedia]? let createdAt: String? } struct DiaryMedia: Decodable, Identifiable { let id: Int let url: String let mediaType: String? let imgWidth: Int? let imgHeight: Int? } struct DiaryCreateBody: Encodable { let datum: String? let typ: String let titel: String? let text: String? let tags: [String]? let gpsLat: Double? let gpsLon: Double? let locationName: String? let isMilestone: Bool } // MARK: - Welcome Dashboard struct DashboardSnapshot: Decodable { let randomPhoto: DashboardPhoto? let lastDiary: DashboardLastDiary? let nextAppointment: DashboardNextAppointment? let lastWeight: DashboardLastWeight? let diaryCount: Int? } struct DashboardPhoto: Decodable { let url: String let previewUrl: String? } struct DashboardLastDiary: Decodable { let titel: String? let datum: String? } struct DashboardNextAppointment: Decodable { let bezeichnung: String? let naechstes: String? let typ: String? } struct DashboardLastWeight: Decodable { let wert: Double? let einheit: String? let datum: String? } // MARK: - Weather struct WeatherForecast: Decodable { let days: [WeatherDay] } struct WeatherDay: Decodable, Identifiable { let date: String let wday: String? let weathercode: Int? let desc: String? let icon: String? let tempMax: Double? let tempMin: Double? let precipProb: Int? let precipSum: Double? let windKmh: Double? let uvIndex: Double? let sunrise: String? let sunset: String? let asphaltTemp: Double? let zecken: String? var id: String { date } } /// Patch body for PATCH /api/routes/{id}. Only non-nil fields are encoded. struct RouteUpdateBody: Encodable { var name: String? var beschreibung: String? var isPublic: Bool? enum CodingKeys: String, CodingKey { case name case beschreibung case isPublic } func encode(to encoder: Encoder) throws { var c = encoder.container(keyedBy: CodingKeys.self) try c.encodeIfPresent(name, forKey: .name) try c.encodeIfPresent(beschreibung, forKey: .beschreibung) try c.encodeIfPresent(isPublic, forKey: .isPublic) } }