diff --git a/BanYaroGo/Assets.xcassets/AppIconHero.imageset/Contents.json b/BanYaroGo/Assets.xcassets/AppIconHero.imageset/Contents.json new file mode 100644 index 0000000..3677a84 --- /dev/null +++ b/BanYaroGo/Assets.xcassets/AppIconHero.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "icon-hero.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/BanYaroGo/Assets.xcassets/AppIconHero.imageset/icon-hero.png b/BanYaroGo/Assets.xcassets/AppIconHero.imageset/icon-hero.png new file mode 100644 index 0000000..bfa2894 Binary files /dev/null and b/BanYaroGo/Assets.xcassets/AppIconHero.imageset/icon-hero.png differ diff --git a/BanYaroGo/Views/LoginView.swift b/BanYaroGo/Views/LoginView.swift index a0e8db9..7b751d3 100644 --- a/BanYaroGo/Views/LoginView.swift +++ b/BanYaroGo/Views/LoginView.swift @@ -7,42 +7,94 @@ struct LoginView: View { @State private var password = "" var body: some View { - VStack(spacing: 24) { - Spacer() + ScrollView { + VStack(spacing: 28) { + hero + pitch + loginCard + registerCard + } + .padding(.horizontal, 24) + .padding(.vertical, 32) + } + .scrollDismissesKeyboard(.interactively) + } - Image(systemName: "pawprint.fill") - .font(.system(size: 80)) + // MARK: - Hero + + private var hero: some View { + VStack(spacing: 12) { + Image("AppIconHero") + .resizable() + .scaledToFit() + .frame(height: 120) + Text("Ban Yaro Go") + .font(.largeTitle.bold()) + Text("Die deutschsprachige Hunde-Plattform — jetzt mit nativem GPS-Tracking.") + .font(.callout) + .foregroundStyle(.secondary) + .multilineTextAlignment(.center) + } + } + + // MARK: - Pitch (für Neue) + + private var pitch: some View { + VStack(alignment: .leading, spacing: 14) { + feature(icon: "map.fill", title: "Gassi-Touren aufzeichnen", subtitle: "GPS-Tracking auch im Hintergrund — mit Pause, Live Activity und HealthKit-Sync.") + feature(icon: "person.2.fill", title: "Hunde-Community", subtitle: "Gassi-Treffen, Tierärzte und Orte in deiner Nähe.") + feature(icon: "book.fill", title: "Tagebuch & Impfpass", subtitle: "Alles rund um deinen Hund an einem Ort.") + feature(icon: "exclamationmark.shield.fill", title: "Giftköder-Alarm", subtitle: "Warnungen aus deiner Region direkt aufs iPhone.") + } + .padding(18) + .frame(maxWidth: .infinity, alignment: .leading) + .background(.background.secondary, in: RoundedRectangle(cornerRadius: 16)) + } + + private func feature(icon: String, title: String, subtitle: String) -> some View { + HStack(alignment: .top, spacing: 12) { + Image(systemName: icon) + .font(.title3) .foregroundStyle(Color.accentColor) - - VStack(spacing: 6) { - Text("Ban Yaro Go") - .font(.largeTitle.bold()) - Text("Melde dich mit deinem banyaro-Account an.") - .font(.callout) + .frame(width: 28) + VStack(alignment: .leading, spacing: 2) { + Text(title).font(.subheadline.bold()) + Text(subtitle) + .font(.caption) .foregroundStyle(.secondary) - .multilineTextAlignment(.center) + } + } + } + + // MARK: - Login (für bestehende User) + + private var loginCard: some View { + VStack(spacing: 12) { + HStack { + Text("Schon angemeldet?") + .font(.headline) + Spacer() } - VStack(spacing: 12) { - TextField("E-Mail", text: $email) - .textContentType(.emailAddress) - .keyboardType(.emailAddress) - .textInputAutocapitalization(.never) - .autocorrectionDisabled() - .padding() - .background(.background.secondary, in: RoundedRectangle(cornerRadius: 12)) + TextField("E-Mail", text: $email) + .textContentType(.emailAddress) + .keyboardType(.emailAddress) + .textInputAutocapitalization(.never) + .autocorrectionDisabled() + .padding() + .background(.background, in: RoundedRectangle(cornerRadius: 12)) - SecureField("Passwort", text: $password) - .textContentType(.password) - .padding() - .background(.background.secondary, in: RoundedRectangle(cornerRadius: 12)) - } + SecureField("Passwort", text: $password) + .textContentType(.password) + .padding() + .background(.background, in: RoundedRectangle(cornerRadius: 12)) if let error = auth.errorMessage { Text(error) .font(.footnote) .foregroundStyle(.red) .multilineTextAlignment(.center) + .frame(maxWidth: .infinity) } Button { @@ -65,31 +117,43 @@ struct LoginView: View { .background(Color.accentColor, in: RoundedRectangle(cornerRadius: 12)) .foregroundStyle(.white) .disabled(auth.isLoggingIn || email.isEmpty || password.isEmpty) - - registrationHint - - Spacer() } - .padding(.horizontal, 28) + .padding(18) + .background(.background.secondary, in: RoundedRectangle(cornerRadius: 16)) } - private var registrationHint: some View { - VStack(spacing: 6) { - Text("Noch kein Account?") + // MARK: - Register (für Neue) + + private var registerCard: some View { + VStack(spacing: 10) { + Text("Neu hier?") + .font(.headline) + Text("banyaro.app ist **kostenlos**, **DSGVO-konform** und wird in Deutschland gehostet — kein App-Store-Konto nötig.") .font(.footnote) .foregroundStyle(.secondary) + .multilineTextAlignment(.center) + if let url = URL(string: "https://banyaro.app/#settings?tab=register") { Link(destination: url) { - HStack(spacing: 6) { - Text("Kostenlos bei banyaro.app registrieren") - .font(.footnote.bold()) - Image(systemName: "arrow.up.right.square") - .font(.caption) + HStack(spacing: 8) { + Image(systemName: "person.crop.circle.badge.plus") + Text("Kostenlos registrieren").bold() + Image(systemName: "arrow.up.right") + .font(.caption.bold()) } - .foregroundStyle(Color.accentColor) + .frame(maxWidth: .infinity, minHeight: 50) } + .background(Color.accentColor.opacity(0.15), in: RoundedRectangle(cornerRadius: 12)) + .foregroundStyle(Color.accentColor) } + + Text("Öffnet die Registrierung im Browser. Danach mit den neuen Zugangsdaten oben einloggen.") + .font(.caption2) + .foregroundStyle(.tertiary) + .multilineTextAlignment(.center) } + .padding(18) + .background(.background.secondary, in: RoundedRectangle(cornerRadius: 16)) } }