banyaro-ios/BanYaroGo/API/DTOs.swift
rene 00dba257b5 Gassi-Treffen-Detail: Fokus auf 'hinfinden'
Detail-Karte zeigt jetzt Treffpunkt + User-Position via UserAnnotation,
darunter eine Navigation-Karte mit:
- Ortsname
- Luftlinien-Distanz vom aktuellen Standort
- 'Route'-Button öffnet Apple Maps (MKMapItem.openInMaps) — User wählt
  dort Walking/Driving

Plus Teilnehmer-Sektion mit GET /api/walks/{id}/participants:
- Liste der Zugesagten ('yes'-RSVP) mit Namen + Hunden
- 'Noch niemand zugesagt'-Hinweis bei leerer Liste
- myRsvp-Status für korrekte Join-Button-Anzeige
- isOwn → 'Dein Treffen'-Badge statt Beitreten-Button

Footer-Hinweis: 'Fotos vom Treffen kannst du später in der banyaro.app
teilen' — Foto-Funktion bewusst PWA-only (User-Wunsch).
2026-05-30 14:16:43 +02:00

358 lines
7.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?
}
// MARK: - Walks (Gassi-Treffen)
struct WalkMeeting: Decodable, Identifiable {
let id: Int
let userId: Int
let titel: String
let datum: String // YYYY-MM-DD
let uhrzeit: String // HH:MM
let lat: Double
let lon: Double
let ortName: String?
let maxTeilnehmer: Int
let beschreibung: String?
let status: String?
let veranstalterName: String?
let teilnehmerCount: Int?
}
struct WalkCreateBody: Encodable {
let titel: String
let datum: String
let uhrzeit: String
let lat: Double
let lon: Double
let ortName: String?
let maxTeilnehmer: Int
let beschreibung: String?
}
struct WalkJoinBody: Encodable {
let dogIds: [Int]
}
struct WalkParticipantsResponse: Decodable {
let invitations: [WalkInvitation]
let myRsvp: String?
let isOrganizer: Bool
}
struct WalkInvitation: Decodable, Identifiable {
let userId: Int
let status: String?
let userName: String?
let hunde: String?
var id: Int { userId }
}
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)
}
}