Files
honeyDueKMP/iosApp/iosApp/PasswordReset/ForgotPasswordView.swift
Trey t 2730c94e4d Add comprehensive error message parsing to prevent raw JSON display
- Created ErrorMessageParser utility for both iOS (Swift) and Android (Kotlin)
- Parser detects JSON-formatted error messages and extracts user-friendly text
- Identifies when data objects (not errors) are returned and provides generic messages
- Updated all API error handling to pass raw error bodies instead of concatenating
- Applied ErrorMessageParser across all ViewModels and screens on both platforms
- Fixed ContractorApi and DocumentApi to not concatenate error bodies with messages
- Updated ApiResultHandler to automatically parse all error messages
- Error messages now show "Request failed. Please check your input and try again." instead of raw JSON

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-14 22:59:42 -06:00

124 lines
4.4 KiB
Swift

import SwiftUI
struct ForgotPasswordView: View {
@ObservedObject var viewModel: PasswordResetViewModel
@FocusState private var isEmailFocused: Bool
@Environment(\.dismiss) var dismiss
var body: some View {
NavigationView {
Form {
// Header Section
Section {
VStack(spacing: 12) {
Image(systemName: "key.fill")
.font(.system(size: 60))
.foregroundStyle(.blue.gradient)
.padding(.vertical)
Text("Forgot Password?")
.font(.title2)
.fontWeight(.bold)
Text("Enter your email address and we'll send you a verification code")
.font(.subheadline)
.foregroundColor(.secondary)
.multilineTextAlignment(.center)
}
.frame(maxWidth: .infinity)
.padding(.vertical)
}
.listRowBackground(Color.clear)
// Email Input Section
Section {
TextField("Email Address", text: $viewModel.email)
.textInputAutocapitalization(.never)
.autocorrectionDisabled()
.keyboardType(.emailAddress)
.focused($isEmailFocused)
.submitLabel(.go)
.onSubmit {
viewModel.requestPasswordReset()
}
.onChange(of: viewModel.email) { _, _ in
viewModel.clearError()
}
} header: {
Text("Email")
} footer: {
Text("We'll send a 6-digit verification code to this address")
}
// Error/Success Messages
if let errorMessage = viewModel.errorMessage {
Section {
Label {
Text(errorMessage)
.foregroundColor(.red)
} icon: {
Image(systemName: "exclamationmark.triangle.fill")
.foregroundColor(.red)
}
}
}
if let successMessage = viewModel.successMessage {
Section {
Label {
Text(successMessage)
.foregroundColor(.green)
} icon: {
Image(systemName: "checkmark.circle.fill")
.foregroundColor(.green)
}
}
}
// Send Code Button
Section {
Button(action: {
viewModel.requestPasswordReset()
}) {
HStack {
Spacer()
if viewModel.isLoading {
ProgressView()
} else {
Label("Send Reset Code", systemImage: "envelope.fill")
.fontWeight(.semibold)
}
Spacer()
}
}
.disabled(viewModel.email.isEmpty || viewModel.isLoading)
Button(action: {
dismiss()
}) {
HStack {
Spacer()
Text("Back to Login")
.foregroundColor(.secondary)
Spacer()
}
}
}
}
.navigationTitle("Reset Password")
.navigationBarTitleDisplayMode(.inline)
.onAppear {
isEmailFocused = true
}
.handleErrors(
error: viewModel.errorMessage,
onRetry: { viewModel.requestPasswordReset() }
)
}
}
}
#Preview {
ForgotPasswordView(viewModel: PasswordResetViewModel())
}