171 lines
5.8 KiB
Swift
171 lines
5.8 KiB
Swift
//
|
|
// SignupView.swift
|
|
// medmap
|
|
//
|
|
|
|
import SwiftUI
|
|
|
|
struct SignupView: View {
|
|
|
|
@StateObject private var viewModel = SignupViewModel()
|
|
@FocusState private var focusedField: Field?
|
|
|
|
private enum Field {
|
|
case email
|
|
case password
|
|
case confirmationPassword
|
|
}
|
|
|
|
var body: some View {
|
|
ZStack {
|
|
Color.appBackground.ignoresSafeArea()
|
|
|
|
VStack(spacing: 0) {
|
|
HStack {
|
|
Image("splashLogo")
|
|
.resizable()
|
|
.scaledToFit()
|
|
.frame(width: 120, height: 76)
|
|
.accessibilityHidden(true)
|
|
Spacer()
|
|
}
|
|
.padding(.top, 32)
|
|
.padding(.horizontal, 32)
|
|
|
|
Spacer()
|
|
|
|
VStack(alignment: .leading, spacing: 24) {
|
|
Text("Criar nova conta")
|
|
.font(.jakartaBold24)
|
|
.foregroundStyle(.white)
|
|
|
|
VStack(spacing: 14) {
|
|
field(
|
|
placeholder: "Email",
|
|
icon: "envelope",
|
|
text: $viewModel.email,
|
|
focusedField: .email,
|
|
contentType: .emailAddress,
|
|
keyboardType: .emailAddress
|
|
)
|
|
field(
|
|
placeholder: "Palavra-passe (min. 8 caracteres)",
|
|
icon: "lock",
|
|
text: $viewModel.password,
|
|
focusedField: .password,
|
|
contentType: .newPassword,
|
|
isSecure: true
|
|
)
|
|
field(
|
|
placeholder: "Confirmar palavra-passe",
|
|
icon: "lock",
|
|
text: $viewModel.confirmationPassword,
|
|
focusedField: .confirmationPassword,
|
|
contentType: .newPassword,
|
|
isSecure: true,
|
|
submitLabel: .go
|
|
) {
|
|
Task { await viewModel.signup() }
|
|
}
|
|
}
|
|
|
|
if let errorMessage = viewModel.errorMessage {
|
|
Text(errorMessage)
|
|
.font(.jakartaRegular14)
|
|
.foregroundStyle(Color.loginErrorText)
|
|
.frame(maxWidth: .infinity)
|
|
.multilineTextAlignment(.center)
|
|
}
|
|
|
|
Button {
|
|
focusedField = nil
|
|
Task { await viewModel.signup() }
|
|
} label: {
|
|
Group {
|
|
if viewModel.isLoading {
|
|
ProgressView().tint(Color.loginButtonText)
|
|
} else {
|
|
Text("Registar").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)
|
|
}
|
|
.padding(.horizontal, 32)
|
|
|
|
Spacer()
|
|
|
|
VStack(spacing: 8) {
|
|
Button("Ja tem conta? Entrar") {
|
|
viewModel.showLogin()
|
|
}
|
|
.font(.jakartaRegular14)
|
|
.foregroundStyle(.white.opacity(0.88))
|
|
|
|
Text("MedMap")
|
|
.font(.jakartaRegular12)
|
|
.foregroundStyle(.white.opacity(0.6))
|
|
}
|
|
.padding(.bottom, 24)
|
|
}
|
|
}
|
|
.navigationBarHidden(true)
|
|
}
|
|
|
|
private func field(
|
|
placeholder: String,
|
|
icon: String,
|
|
text: Binding<String>,
|
|
focusedField: Field,
|
|
contentType: UITextContentType,
|
|
keyboardType: UIKeyboardType = .default,
|
|
isSecure: Bool = false,
|
|
submitLabel: SubmitLabel = .next,
|
|
onSubmit: @escaping () -> Void = {}
|
|
) -> some View {
|
|
HStack(spacing: 10) {
|
|
Image(systemName: icon)
|
|
.foregroundStyle(.white)
|
|
.frame(width: 18)
|
|
ZStack(alignment: .leading) {
|
|
if text.wrappedValue.isEmpty {
|
|
Text(placeholder).foregroundStyle(Color.loginPlaceholder)
|
|
}
|
|
if isSecure {
|
|
SecureField("", text: text)
|
|
} else {
|
|
TextField("", text: text)
|
|
.keyboardType(keyboardType)
|
|
.textInputAutocapitalization(.never)
|
|
.autocorrectionDisabled()
|
|
}
|
|
}
|
|
.foregroundStyle(.white)
|
|
.tint(.white)
|
|
.font(.jakartaRegular16)
|
|
.textContentType(contentType)
|
|
.focused($focusedField, equals: focusedField)
|
|
.submitLabel(submitLabel)
|
|
.onSubmit(onSubmit)
|
|
}
|
|
.padding(.horizontal, 14)
|
|
.frame(height: 46)
|
|
.background(Color.loginFieldBackground)
|
|
.overlay {
|
|
RoundedRectangle(cornerRadius: 8)
|
|
.stroke(Color.loginFieldBorder, lineWidth: 1)
|
|
}
|
|
.clipShape(RoundedRectangle(cornerRadius: 8))
|
|
.accessibilityLabel(placeholder)
|
|
}
|
|
}
|
|
|
|
#Preview {
|
|
SignupView()
|
|
} |