Not has address

This commit is contained in:
Daniel Arantes Loverde
2026-02-07 17:16:45 -03:00
parent 92c2a67736
commit 54686397b9
28 changed files with 2115 additions and 498 deletions

View File

@@ -0,0 +1,148 @@
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 {
#if os(Android)
SwiftUI.Image("pedifoods")
.resizable()
#else
SwiftUI.Image("pedifoods")
.resizable()
#endif
}
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", text: $email)
LoginField(icon: "phone", placeholder: "(00) 00000-0000", 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 \nou 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()
}
}
.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
@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)
}
.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)
)
}
}