Add onboarding UI tests and improve app data management

- Add Suite0_OnboardingTests with fresh install and login test flows
- Add accessibility identifiers to onboarding views for UI testing
- Remove deprecated DataCache in favor of unified DataManager
- Update API layer to support public upgrade-triggers endpoint
- Improve onboarding first task view with better date handling
- Update various views with accessibility identifiers for testing
- Fix subscription feature comparison view layout
- Update document detail view improvements

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

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Trey t
2025-12-04 15:55:34 -06:00
parent 43f5b9514f
commit fff1032c29
43 changed files with 1055 additions and 923 deletions

View File

@@ -3,9 +3,10 @@ import ComposeApp
struct ResidenceDetailView: View {
let residenceId: Int32
@StateObject private var viewModel = ResidenceViewModel()
@StateObject private var taskViewModel = TaskViewModel()
@ObservedObject private var dataManager = DataManagerObservable.shared
// Use TaskViewModel's state instead of local state
private var tasksResponse: TaskColumnsResponse? { taskViewModel.tasksResponse }
@@ -15,7 +16,7 @@ struct ResidenceDetailView: View {
@State private var contractors: [ContractorSummary] = []
@State private var isLoadingContractors = false
@State private var contractorsError: String?
@State private var showAddTask = false
@State private var showEditResidence = false
@State private var showEditTask = false
@@ -37,7 +38,7 @@ struct ResidenceDetailView: View {
// Check if current user is the owner of the residence
private func isCurrentUserOwner(of residence: ResidenceResponse) -> Bool {
guard let currentUser = ComposeApp.DataCache.shared.currentUser.value else {
guard let currentUser = dataManager.currentUser else {
return false
}
return Int(residence.ownerId) == Int(currentUser.id)

View File

@@ -134,28 +134,52 @@ class ResidenceViewModel: ObservableObject {
}
func createResidence(request: ResidenceCreateRequest, completion: @escaping (Bool) -> Void) {
createResidence(request: request) { result in
completion(result != nil)
}
}
/// Creates a residence and returns the created residence on success
func createResidence(request: ResidenceCreateRequest, completion: @escaping (ResidenceResponse?) -> Void) {
isLoading = true
errorMessage = nil
Task {
do {
print("🏠 ResidenceVM: Calling API...")
let result = try await APILayer.shared.createResidence(request: request)
print("🏠 ResidenceVM: Got result: \(String(describing: result))")
if result is ApiResultSuccess<ResidenceResponse> {
self.isLoading = false
// DataManager is updated by APILayer (including refreshMyResidences),
// which updates DataManagerObservable, which updates our @Published
// myResidences via Combine subscription
completion(true)
} else if let error = result as? ApiResultError {
self.errorMessage = ErrorMessageParser.parse(error.message)
self.isLoading = false
completion(false)
await MainActor.run {
if let success = result as? ApiResultSuccess<ResidenceResponse> {
print("🏠 ResidenceVM: Is ApiResultSuccess, data = \(String(describing: success.data))")
if let residence = success.data {
print("🏠 ResidenceVM: Got residence with id \(residence.id)")
self.isLoading = false
completion(residence)
} else {
print("🏠 ResidenceVM: success.data is nil")
self.isLoading = false
completion(nil)
}
} else if let error = result as? ApiResultError {
print("🏠 ResidenceVM: Is ApiResultError: \(error.message ?? "nil")")
self.errorMessage = ErrorMessageParser.parse(error.message)
self.isLoading = false
completion(nil)
} else {
print("🏠 ResidenceVM: Unknown result type: \(type(of: result))")
self.isLoading = false
completion(nil)
}
}
} catch {
self.errorMessage = error.localizedDescription
self.isLoading = false
completion(false)
print("🏠 ResidenceVM: Exception: \(error)")
await MainActor.run {
self.errorMessage = error.localizedDescription
self.isLoading = false
completion(nil)
}
}
}
}