Sechs Offline-Features: Erste Hilfe, Ausgaben, Wetter, Gassi-Zeiten, Giftköder, Verlorene
Pitch-Karte erweitert um die neuen Features (sowie Hundesitting, Züchter).
Neue DTOs in DTOs.swift:
- Expense + ExpenseCreateBody
- GassiZeit + GassiZeitCreateBody (mit wochentage [String], radius_m)
- PoisonAlert + PoisonCreateBody
- LostDog + LostDogCreateBody
- WeatherForecast + WeatherDay (mit asphalt_temp, zecken, pollen-Felder)
Neue Views:
- ErsteHilfeView + Detail: sechs Notfall-Topics (Vergiftung, Hitzschlag,
Wunden, Atemnot, Krampfanfall, Magendrehung) — komplett offline, kein API
- AusgabenView: Liste mit Total, AddExpenseSheet mit Kategorie/Betrag/
Datum/Hund-Picker
- WetterView: One-Shot Location + /api/weather/forecast, 7-Tage-Vorhersage
mit Hunde-Tipps (Hitze ab 25°/30°, Frost, Asphalt ≥50°, Zecken, Regen)
- GassiZeitenView: eigene Zeiten + Add-Sheet (Wochentag-Picker, Hund-
Auswahl), automatische lokale UNCalendarNotifications via Scheduler
- GiftkoederView: Map mit Pins + Liste in 5km Umkreis, Report-Sheet mit
Typ-Auswahl
- VerloreneHundeView: Liste mit Foto/Distanz, Detail mit Karte
Support:
- OneShotLocation: kleiner CLLocationManager-Wrapper für einmalige
Positionsabfrage (Wetter, Giftköder)
- GassiZeitenScheduler: UNCalendarNotificationTrigger pro Wochentag,
Identifier-Schema "gz-{id}-{weekday}"
Navigation: Section "Hund & Alltag" im Mehr-Tab mit NavigationLinks zu
allen sechs neuen Ansichten.
This commit is contained in:
parent
f1b3ff4035
commit
68b084be97
11 changed files with 1547 additions and 0 deletions
270
BanYaroGo/Views/ErsteHilfeView.swift
Normal file
270
BanYaroGo/Views/ErsteHilfeView.swift
Normal file
|
|
@ -0,0 +1,270 @@
|
|||
import SwiftUI
|
||||
|
||||
struct ErsteHilfeView: View {
|
||||
var body: some View {
|
||||
List {
|
||||
Section {
|
||||
HStack(spacing: 12) {
|
||||
Image(systemName: "phone.fill")
|
||||
.foregroundStyle(.red)
|
||||
VStack(alignment: .leading) {
|
||||
Text("Im Ernstfall sofort den Tierarzt anrufen!").font(.subheadline.bold())
|
||||
Text("Diese Hinweise ersetzen keine tierärztliche Behandlung.")
|
||||
.font(.caption)
|
||||
.foregroundStyle(.secondary)
|
||||
}
|
||||
}
|
||||
.padding(.vertical, 4)
|
||||
}
|
||||
|
||||
ForEach(ErsteHilfeContent.topics) { topic in
|
||||
NavigationLink {
|
||||
ErsteHilfeDetailView(topic: topic)
|
||||
} label: {
|
||||
HStack(spacing: 14) {
|
||||
Image(systemName: topic.icon)
|
||||
.font(.title3)
|
||||
.foregroundStyle(topic.tint)
|
||||
.frame(width: 32)
|
||||
VStack(alignment: .leading, spacing: 2) {
|
||||
Text(topic.title).font(.headline)
|
||||
Text(topic.summary)
|
||||
.font(.caption)
|
||||
.foregroundStyle(.secondary)
|
||||
.lineLimit(2)
|
||||
}
|
||||
}
|
||||
.padding(.vertical, 4)
|
||||
}
|
||||
}
|
||||
}
|
||||
.navigationTitle("Erste Hilfe")
|
||||
.navigationBarTitleDisplayMode(.inline)
|
||||
}
|
||||
}
|
||||
|
||||
private struct ErsteHilfeDetailView: View {
|
||||
let topic: ErsteHilfeTopic
|
||||
|
||||
var body: some View {
|
||||
ScrollView {
|
||||
VStack(alignment: .leading, spacing: 18) {
|
||||
HStack(spacing: 14) {
|
||||
Image(systemName: topic.icon)
|
||||
.font(.system(size: 44))
|
||||
.foregroundStyle(topic.tint)
|
||||
.frame(width: 70, height: 70)
|
||||
.background(topic.tint.opacity(0.15), in: Circle())
|
||||
VStack(alignment: .leading, spacing: 4) {
|
||||
Text(topic.title).font(.title2.bold())
|
||||
Text(topic.summary)
|
||||
.font(.subheadline)
|
||||
.foregroundStyle(.secondary)
|
||||
}
|
||||
}
|
||||
|
||||
ForEach(Array(topic.sections.enumerated()), id: \.offset) { _, section in
|
||||
VStack(alignment: .leading, spacing: 8) {
|
||||
Text(section.heading).font(.headline)
|
||||
VStack(alignment: .leading, spacing: 6) {
|
||||
ForEach(Array(section.steps.enumerated()), id: \.offset) { i, step in
|
||||
HStack(alignment: .firstTextBaseline, spacing: 10) {
|
||||
Text("\(i + 1).")
|
||||
.font(.subheadline.bold())
|
||||
.foregroundStyle(topic.tint)
|
||||
.frame(width: 20, alignment: .trailing)
|
||||
Text(step)
|
||||
.font(.subheadline)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.padding()
|
||||
.background(.background.secondary, in: RoundedRectangle(cornerRadius: 12))
|
||||
}
|
||||
|
||||
if !topic.warnings.isEmpty {
|
||||
VStack(alignment: .leading, spacing: 6) {
|
||||
Label("Sofort zum Tierarzt", systemImage: "exclamationmark.triangle.fill")
|
||||
.font(.headline)
|
||||
.foregroundStyle(.red)
|
||||
ForEach(topic.warnings, id: \.self) { w in
|
||||
Text("• \(w)").font(.subheadline)
|
||||
}
|
||||
}
|
||||
.padding()
|
||||
.background(Color.red.opacity(0.08), in: RoundedRectangle(cornerRadius: 12))
|
||||
}
|
||||
}
|
||||
.padding()
|
||||
}
|
||||
.navigationTitle(topic.title)
|
||||
.navigationBarTitleDisplayMode(.inline)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Content
|
||||
|
||||
struct ErsteHilfeTopic: Identifiable {
|
||||
let id: String
|
||||
let title: String
|
||||
let summary: String
|
||||
let icon: String
|
||||
let tint: Color
|
||||
let sections: [Section]
|
||||
let warnings: [String]
|
||||
|
||||
struct Section {
|
||||
let heading: String
|
||||
let steps: [String]
|
||||
}
|
||||
}
|
||||
|
||||
enum ErsteHilfeContent {
|
||||
static let topics: [ErsteHilfeTopic] = [
|
||||
ErsteHilfeTopic(
|
||||
id: "vergiftung",
|
||||
title: "Vergiftung",
|
||||
summary: "Schokolade, Trauben, Giftköder, Frostschutz, Medikamente.",
|
||||
icon: "exclamationmark.octagon.fill",
|
||||
tint: .red,
|
||||
sections: [
|
||||
.init(heading: "Sofortmaßnahmen", steps: [
|
||||
"Hund von der Giftquelle entfernen. Reste sichern (Foto/Verpackung).",
|
||||
"NICHT zum Erbrechen bringen, außer der Tierarzt sagt es ausdrücklich.",
|
||||
"Tierarzt oder Tierklinik anrufen — Art und Menge des Gifts angeben.",
|
||||
"Bei Bewusstlosigkeit: stabile Seitenlage, Atemwege freihalten."
|
||||
]),
|
||||
.init(heading: "Was du nicht tun solltest", steps: [
|
||||
"Kein Salzwasser, keine Milch, kein Öl geben — kann gefährlich sein.",
|
||||
"Nicht abwarten, ob Symptome verschwinden."
|
||||
])
|
||||
],
|
||||
warnings: [
|
||||
"Krämpfe, Erbrechen, Durchfall, Speicheln, Apathie",
|
||||
"Schaum vor dem Maul, Blutungen",
|
||||
"Verdacht auf Giftköder (Wurst mit Glas, Nägel, Tabletten)"
|
||||
]
|
||||
),
|
||||
ErsteHilfeTopic(
|
||||
id: "hitzschlag",
|
||||
title: "Hitzschlag",
|
||||
summary: "Hecheln, Taumeln, Bewusstseinsverlust durch Überhitzung.",
|
||||
icon: "thermometer.sun.fill",
|
||||
tint: .orange,
|
||||
sections: [
|
||||
.init(heading: "Sofort kühlen", steps: [
|
||||
"Hund in den Schatten oder kühlen Raum bringen.",
|
||||
"Pfoten, Innenschenkel und Bauch mit lauwarmem (nicht eiskaltem!) Wasser kühlen.",
|
||||
"Kleine Schlucke Wasser anbieten, nicht zwingen.",
|
||||
"Auf dem Weg zum Tierarzt weiterkühlen — feuchte Tücher auf den Körper."
|
||||
])
|
||||
],
|
||||
warnings: [
|
||||
"Körpertemperatur über 40 °C",
|
||||
"Verwirrung, Krämpfe, Erbrechen",
|
||||
"Zahnfleisch dunkelrot, blau oder bleich"
|
||||
]
|
||||
),
|
||||
ErsteHilfeTopic(
|
||||
id: "wunde",
|
||||
title: "Wunden & Blutungen",
|
||||
summary: "Schnitte, Bisse, Verletzungen an Pfoten und Körper.",
|
||||
icon: "bandage.fill",
|
||||
tint: .pink,
|
||||
sections: [
|
||||
.init(heading: "Bei starker Blutung", steps: [
|
||||
"Druckverband mit sauberem Tuch anlegen — fest, aber nicht abschnüren.",
|
||||
"Bei spritzendem Blut: oberhalb der Wunde mit den Fingern abdrücken.",
|
||||
"Pfote/Bein hochlagern, ruhig halten.",
|
||||
"Sofort zum Tierarzt."
|
||||
]),
|
||||
.init(heading: "Kleine Wunden", steps: [
|
||||
"Fremdkörper (Glas, Splitter) NICHT selbst rausziehen, wenn tief.",
|
||||
"Wunde mit klarem Wasser ausspülen.",
|
||||
"Trocken tupfen, locker abdecken — Hund vom Lecken abhalten."
|
||||
])
|
||||
],
|
||||
warnings: [
|
||||
"Stark blutende oder pumpende Wunde",
|
||||
"Fremdkörper steckt fest",
|
||||
"Tiefe Bisswunden (auch wenn klein) → Infektionsgefahr"
|
||||
]
|
||||
),
|
||||
ErsteHilfeTopic(
|
||||
id: "atemnot",
|
||||
title: "Atemnot & Bewusstlosigkeit",
|
||||
summary: "Erstickung, Würgen, Kollaps, Reanimation.",
|
||||
icon: "lungs.fill",
|
||||
tint: .blue,
|
||||
sections: [
|
||||
.init(heading: "Fremdkörper im Hals", steps: [
|
||||
"Maul vorsichtig öffnen, mit Taschenlampe schauen.",
|
||||
"Sichtbaren Fremdkörper mit den Fingern (nicht Pinzette!) lösen.",
|
||||
"Bei kleinem Hund: Kopf nach unten halten und zwischen die Schulterblätter klopfen.",
|
||||
"Bei großem Hund: Heimlich-Manöver — von hinten umfassen, ruckartig nach oben drücken."
|
||||
]),
|
||||
.init(heading: "Reanimation (CPR)", steps: [
|
||||
"Hund auf rechte Seite legen, Atemwege kontrollieren.",
|
||||
"Maul schließen, in die Nase atmen — Brustkorb soll sich heben.",
|
||||
"30 Herzdruckmassagen (auf Brustkorb-Höhe der Schulterblätter), dann 2 Beatmungen.",
|
||||
"Weiter bis Atmung einsetzt oder Tierarzt übernimmt."
|
||||
])
|
||||
],
|
||||
warnings: [
|
||||
"Bewusstlosigkeit, keine Atmung, kein Puls",
|
||||
"Blaue Schleimhäute",
|
||||
"Würgen ohne Erfolg über mehrere Minuten"
|
||||
]
|
||||
),
|
||||
ErsteHilfeTopic(
|
||||
id: "krampfanfall",
|
||||
title: "Krampfanfall",
|
||||
summary: "Epileptischer Anfall, Zittern, Bewusstseinsstörung.",
|
||||
icon: "waveform.path.ecg",
|
||||
tint: .purple,
|
||||
sections: [
|
||||
.init(heading: "Während des Anfalls", steps: [
|
||||
"Ruhe bewahren, Zeit messen (Dauer ist wichtig für den Tierarzt).",
|
||||
"Umgebung sichern — Möbel/Kanten wegräumen.",
|
||||
"NICHT festhalten, NICHTS ins Maul stecken.",
|
||||
"Licht, Geräusche und Reize reduzieren."
|
||||
]),
|
||||
.init(heading: "Nach dem Anfall", steps: [
|
||||
"Hund kann verwirrt, blind oder unruhig sein — Zeit geben.",
|
||||
"Wasser bereitstellen, in eine ruhige Ecke legen.",
|
||||
"Auch nach erstem Anfall zum Tierarzt — Ursache abklären."
|
||||
])
|
||||
],
|
||||
warnings: [
|
||||
"Anfall dauert länger als 5 Minuten (Notfall!)",
|
||||
"Mehrere Anfälle in Folge",
|
||||
"Erster Anfall überhaupt"
|
||||
]
|
||||
),
|
||||
ErsteHilfeTopic(
|
||||
id: "magendrehung",
|
||||
title: "Magendrehung",
|
||||
summary: "Akuter Notfall, vor allem bei großen Rassen.",
|
||||
icon: "stomach.fill",
|
||||
tint: .red,
|
||||
sections: [
|
||||
.init(heading: "Erkennen", steps: [
|
||||
"Aufgeblähter, harter Bauch.",
|
||||
"Erfolgloses Würgen ohne Erbrechen.",
|
||||
"Unruhe, Speicheln, Atemnot.",
|
||||
"Häufig nach dem Fressen / Trinken großer Mengen."
|
||||
]),
|
||||
.init(heading: "Sofort handeln", steps: [
|
||||
"JEDE Minute zählt — direkt zum Notfall-Tierarzt fahren.",
|
||||
"Beim Transport ruhig halten, nicht füttern, nicht tränken.",
|
||||
"Vorher anrufen, damit OP-Team bereitsteht."
|
||||
])
|
||||
],
|
||||
warnings: [
|
||||
"Symptome wie oben → IMMER Notfall",
|
||||
"Ohne OP innerhalb weniger Stunden tödlich"
|
||||
]
|
||||
)
|
||||
]
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue