KasztelanKarta/Kasztelan/AuthView.swift
2026-10-16 23:37:49 +01:00

130 lines
6.1 KiB
Swift

import SwiftUI
struct AuthView: View {
@EnvironmentObject private var model: AppModel
@State private var registering = false
@State private var login = ""
@State private var password = ""
@State private var form = RegistrationForm()
var body: some View {
ScrollView {
VStack(spacing: 14) {
HStack {
Image("Logo")
.resizable()
.scaledToFit()
.frame(height: 58)
Spacer()
LanguageButton()
}
.padding(.bottom, 18)
if registering { registrationForm }
else { loginForm }
}
.padding(22)
}
.scrollDismissesKeyboard(.interactively)
.background(KTheme.background)
}
private var loginForm: some View {
VStack(alignment: .leading, spacing: 14) {
Text(model.text("Zaloguj się", "Sign in")).font(.largeTitle.bold())
Text(model.text("Twoja karta i punkty będą dostępne po zalogowaniu.", "Your card and points will be available after signing in."))
.foregroundStyle(.secondary)
KasztelanField(title: model.text("E-mail lub login", "Email or username"), text: $login, keyboard: .emailAddress, contentType: .username)
KasztelanField(title: model.text("Hasło", "Password"), text: $password, secure: true, contentType: .password)
Button(model.text("Zaloguj", "Sign in")) {
guard !login.trimmingCharacters(in: .whitespaces).isEmpty, !password.isEmpty else {
model.errorMessage = model.text("Wpisz login i hasło.", "Enter your username and password.")
return
}
Task { await model.login(login: login, password: password) }
}
.buttonStyle(PrimaryButtonStyle())
Link(destination: URL(string: "https://kasztelan.co.uk/forgot/")!) {
HStack(spacing: 8) {
Image(systemName: "key.fill")
Text(model.text("Nie pamiętasz hasła?", "Forgot your password?"))
}
.font(.subheadline.bold())
.foregroundStyle(KTheme.orange)
.frame(maxWidth: .infinity, minHeight: 46)
}
.accessibilityLabel(model.text("Otwórz resetowanie hasła", "Open password reset"))
Button(model.text("Załóż konto", "Create account")) { registering = true }
.frame(maxWidth: .infinity, minHeight: 50)
.background(KTheme.panel)
.clipShape(RoundedRectangle(cornerRadius: 16))
}
}
private var registrationForm: some View {
VStack(alignment: .leading, spacing: 12) {
Text(model.text("Załóż konto", "Create account")).font(.largeTitle.bold())
KasztelanField(title: model.text("Imię", "First name"), text: $form.firstName, contentType: .givenName)
KasztelanField(title: model.text("Nazwisko", "Last name"), text: $form.lastName, contentType: .familyName)
KasztelanField(title: "E-mail", text: $form.email, keyboard: .emailAddress, contentType: .emailAddress)
KasztelanField(title: model.text("Hasło", "Password"), text: $form.password, secure: true, contentType: .newPassword)
KasztelanField(title: model.text("Telefon UK lub PL", "UK or Polish phone number"), text: $form.mobile, keyboard: .phonePad, contentType: .telephoneNumber)
KasztelanField(title: model.text("Kod pocztowy UK", "UK postcode"), text: $form.postcode, contentType: .postalCode)
KasztelanField(title: model.text("Data urodzenia: RRRR-MM-DD", "Date of birth: YYYY-MM-DD"), text: dateBinding, keyboard: .numberPad)
KasztelanField(title: model.text("Numer karty (opcjonalnie)", "Card number (optional)"), text: $form.cardNumber, keyboard: .numberPad)
Toggle(model.text("Akceptuję regulamin", "I accept the terms"), isOn: $form.acceptsTerms)
.tint(KTheme.orange)
HStack(spacing: 16) {
Link(model.text("Regulamin", "Terms"), destination: URL(string: "https://kasztelan.co.uk/regulamin/")!)
Link(model.text("Polityka prywatności", "Privacy policy"), destination: URL(string: "https://kasztelan.co.uk/privacy-policy/")!)
}
.font(.subheadline.bold())
.foregroundStyle(KTheme.orange)
Button(model.text("Utwórz konto", "Create account")) {
Task { await model.register(form) }
}
.buttonStyle(PrimaryButtonStyle())
Button(model.text("Mam już konto", "I already have an account")) { registering = false }
.frame(maxWidth: .infinity, minHeight: 50)
.background(KTheme.panel)
.clipShape(RoundedRectangle(cornerRadius: 16))
}
}
private var dateBinding: Binding<String> {
Binding(
get: { form.dob },
set: { form.dob = Self.formatDateInput($0) }
)
}
static func formatDateInput(_ input: String) -> String {
let digits = String(input.filter(\.isNumber).prefix(8))
var result = ""
for (index, character) in digits.enumerated() {
if index == 4 || index == 6 { result.append("-") }
result.append(character)
}
return result
}
}
struct LanguageButton: View {
@EnvironmentObject private var model: AppModel
var body: some View {
Button {
model.language = model.language == .pl ? .en : .pl
} label: {
Text(model.language == .pl ? "EN" : "PL")
.font(.subheadline.bold())
.foregroundStyle(KTheme.orange)
.frame(width: 44, height: 44, alignment: .center)
.background(KTheme.panel, in: Circle())
.contentShape(Circle())
}
.buttonStyle(.plain)
.accessibilityLabel(model.text("Zmień język na angielski", "Change language to Polish"))
}
}