banyaro-ios/BanYaroGo/API/DTOs.swift
rene 3373305b23 Tagebuch-Decode-Fix: media_items statt media, is_milestone als Int 0/1
Backend liefert das Foto-Array unter 'media_items' (in _entry_dict),
nicht 'media'. Außerdem ist is_milestone wieder eine SQLite-Int-Spalte
0/1 — der bekannte Bool-Quirk. Mit Bool? in der DTO bricht die Decoder
ab und die ganze Liste verschwindet stillschweigend in den Fehler-Zustand.
2026-05-30 12:35:50 +02:00

311 lines
6.5 KiB
Swift

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?
// is_milestone kommt als SQLite-Int 0/1 Backend bool-konvertiert es nicht.
let isMilestone: Int?
let mediaItems: [DiaryMedia]?
let createdAt: String?
var isMilestoneFlag: Bool { isMilestone == 1 }
}
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)
}
}