// // LoginView.swift // medmap // import SwiftUI struct LoginView: View { @StateObject private var viewModel = LoginViewModel() @FocusState private var focusedField: Field? private enum Field { case email case password } var body: some View { ZStack { Color.appBackground.ignoresSafeArea() formPanel } //.preferredColorScheme(.light) } private var formPanel: some View { VStack(spacing: 0) { HStack { Image("splashLogo") .resizable() .scaledToFit() .frame(width: 150, height: 180) .accessibilityHidden(true) Spacer() } .padding(.top, 32) .padding(.horizontal, 32) Spacer() VStack(alignment: .leading) { Text("Aceda aqui à sua conta") .font(.jakartaBold24) .foregroundStyle(.white) VStack(spacing: 14) { loginField( placeholder: "Email", icon: "envelope", text: $viewModel.email, field: .email, contentType: .emailAddress, keyboardType: .emailAddress ) passwordField } if let errorMessage = viewModel.errorMessage { Text(errorMessage) .font(.jakartaRegular14) .foregroundStyle(Color.loginErrorText) .frame(maxWidth: .infinity) .multilineTextAlignment(.center) .fixedSize(horizontal: false, vertical: true) .accessibilityLabel("Erro de autenticacao: \(errorMessage)") } Button { focusedField = nil Task { await viewModel.login() } } label: { Group { if viewModel.isLoading { ProgressView() .tint(Color.loginButtonText) } else { Text("Entrar") .font(.jakartaBold16) } } .frame(maxWidth: .infinity) .frame(height: 46) } .foregroundStyle(Color.loginButtonText) .background(Color.loginButtonBackground) .clipShape(RoundedRectangle(cornerRadius: 8)) .disabled(!viewModel.canSubmit) .opacity(viewModel.canSubmit ? 1 : 0.55) .accessibilityHint("Inicia sessao na conta de doente") } .padding(.horizontal, 32) Spacer() Spacer() VStack(spacing: 8) { Button("Não tem conta? Criar conta") { viewModel.showSignup() } .font(.jakartaRegular14) .foregroundStyle(.white.opacity(0.88)) Text("MedMap") .font(.jakartaRegular12) .foregroundStyle(.white.opacity(0.6)) Image("corporateLogos") } .padding(.bottom, 12) } } private var passwordField: some View { loginFieldContainer(icon: "lock") { ZStack(alignment: .leading) { if viewModel.password.isEmpty { Text("Palavra-passe") .foregroundStyle(Color.loginPlaceholder) } SecureField("", text: $viewModel.password) .foregroundStyle(.white) .tint(.white) .textContentType(.password) .focused($focusedField, equals: .password) .submitLabel(.go) .onSubmit { Task { await viewModel.login() } } } } .accessibilityLabel("Palavra-passe") } private func loginField( placeholder: String, icon: String, text: Binding, field: Field, contentType: UITextContentType, keyboardType: UIKeyboardType ) -> some View { loginFieldContainer(icon: icon) { ZStack(alignment: .leading) { if text.wrappedValue.isEmpty { Text(placeholder) .foregroundStyle(Color.loginPlaceholder) } TextField("", text: text) .foregroundStyle(.white) .tint(.white) .textContentType(contentType) .keyboardType(keyboardType) .textInputAutocapitalization(.never) .autocorrectionDisabled() .focused($focusedField, equals: field) .submitLabel(.next) .onSubmit { focusedField = .password } } } .accessibilityLabel(placeholder) } private func loginFieldContainer( icon: String, @ViewBuilder content: () -> Content ) -> some View { HStack(spacing: 10) { Image(systemName: icon) .foregroundStyle(.white) .frame(width: 18) content() .font(.jakartaRegular16) } .padding(.horizontal, 14) .frame(height: 46) .background(Color.loginFieldBackground) .overlay { RoundedRectangle(cornerRadius: 8) .stroke(Color.loginFieldBorder, lineWidth: 1) } .clipShape(RoundedRectangle(cornerRadius: 8)) } } #Preview { LoginView() }