import SwiftUI struct LoginEmailView: View { @Binding var root: RootFlow @Binding var selectedTab: MainTab let tokenStore: TokenStore @Binding var appState: AppState let navigate: (Route) -> Void @State var email = "" @State var phone = "" @State var isLoading = false @State var errorMessage: String? @Environment(\.dismiss) var dismiss @Environment(\.colorScheme) var colorScheme @ViewBuilder private var logoImage: some View { SwiftUI.Image("pedifoods") .resizable() } var body: some View { ZStack { (colorScheme == .dark ? Color.black : AppColors.backgroundLight).ignoresSafeArea() VStack(spacing: 0) { logoImage .scaledToFit() .frame(width: 120, height: 120) Text("Boas-vindas!") .font(AppTypography.heading1) .foregroundStyle(colorScheme == .dark ? Color.white : AppColors.textPrimary) .padding(Edge.Set.top, 8) .padding(.bottom, 16) VStack(spacing: 16) { VStack(alignment: .leading, spacing: 16) { LoginField(icon: "envelope", placeholder: "seu@email.com", keyboardType: .emailAddress, text: $email) LoginField(icon: "phone", placeholder: "(00) 00000-0000", keyboardType: .numberPad, text: $phone) .onChange(of: phone) { _, newValue in let masked = formatPhoneBR(newValue) if masked != newValue { phone = masked } } } } .padding(.horizontal, 24) .padding(.bottom, 16) PrimaryButton(title: "Receber Código", image: Image(systemName: "arrow.right")) { requestOtp() } .padding(.horizontal, 24) .tint(AppColors.tertiary) .disabled(isLoading || email.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty || normalizePhoneNumberForAPI(phone).isEmpty) .opacity((isLoading || email.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty || normalizePhoneNumberForAPI(phone).isEmpty) ? 0.6 : 1.0) Text("Enviaremos um código de verificação por SMS \ne E-mail para confirmar seu acesso.") .font(.caption) .foregroundStyle(Color.gray) .multilineTextAlignment(.center) .padding(.horizontal, 40) .padding([.top, .bottom], 16) HStack(spacing: 6) { Text("Novo por aqui?") .foregroundStyle(colorScheme == .dark ? Color.white.opacity(0.8) : Color.gray) Text("Crie sua conta") .foregroundStyle(AppColors.primary) .onTapGesture { dismiss() } } .buttonStyle(.plain) .font(AppTypography.body) .padding(.top, 8) Spacer() } } } private func requestOtp() { let sanitizedEmail = email.trimmingCharacters(in: .whitespacesAndNewlines) let normalizedPhone = normalizePhoneNumberForAPI(phone) guard !sanitizedEmail.isEmpty, !normalizedPhone.isEmpty else { return } isLoading = true errorMessage = nil Task { do { let service = ApiService() let response = try await service.requestOtp(email: sanitizedEmail, phoneNumber: normalizedPhone) await MainActor.run { if response.error { isLoading = false let message = response.message ?? "Nao foi possivel enviar o codigo." errorMessage = message SnackbarCenter.shared.show(title: message, style: .error, icon: "xmark.octagon.fill", duration: 4.0) return } isLoading = false SnackbarCenter.shared.show(title: "Codigo enviado com sucesso.", style: .info, icon: "paperplane.fill", duration: 3.0) appState.profile.email = sanitizedEmail appState.profile.phone = phone navigate(.otp(email: sanitizedEmail, phoneNumber: normalizedPhone)) } } catch { await MainActor.run { isLoading = false let message = userFacingAuthErrorMessage(error) errorMessage = message SnackbarCenter.shared.show(title: message, style: .error, icon: "xmark.octagon.fill", duration: 4.0) } } } } } struct LoginField: View { let icon: String let placeholder: String let keyboardType: UIKeyboardType @Binding var text: String var body: some View { HStack(spacing: 12) { Image(systemName: icon) .foregroundStyle(Color.gray) .frame(width: 28) TextField(text: $text, prompt: Text(placeholder).foregroundColor(Color.gray)) { } .appNoAutoCap() .foregroundColor(.black) .keyboardType(keyboardType) } .padding(.horizontal, 16) .frame(height: 52) .background(Color.white) .clipShape(RoundedRectangle(cornerRadius: 16, style: .continuous)) .overlay( RoundedRectangle(cornerRadius: 16, style: .continuous) .stroke(Color.black.opacity(0.06), lineWidth: 1) ) } }