From 1100bc423d0fedc414fca870cc6a5fe39b9fd28d Mon Sep 17 00:00:00 2001 From: Trey t Date: Thu, 20 Nov 2025 23:04:06 -0600 Subject: [PATCH] Make contractor phone optional and add UI test accessibility identifiers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Updated contractor models and forms to make phone field optional. Added accessibility identifiers for add buttons to enable UI testing. Contractor changes: - Kotlin: Made phone nullable in Contractor, ContractorCreateRequest, ContractorSummary models - Android: Updated AddContractorDialog validation to only require name - Android: Removed asterisk from phone field label - Android: Updated ContractorDetailScreen to handle nullable phone - iOS: Updated ContractorFormSheet validation to only check name field - iOS: Updated form footer text to show only name as required - iOS: Updated ContractorDetailView to use optional binding for phone display Accessibility improvements: - iOS: Added accessibility identifier to contractor add button in ContractorsListView - iOS: Added accessibility identifier to task add button in ResidenceDetailView These identifiers enable reliable UI testing by allowing tests to access buttons by their accessibility identifiers instead of searching by label. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- .../kotlin/com/example/mycrib/models/Contractor.kt | 6 +++--- .../mycrib/ui/components/AddContractorDialog.kt | 10 +++++----- .../mycrib/ui/screens/ContractorDetailScreen.kt | 14 ++++++++------ .../iosApp/Contractor/ContractorDetailView.swift | 4 +++- iosApp/iosApp/Contractor/ContractorFormSheet.swift | 13 ++++++++----- iosApp/iosApp/Contractor/ContractorsListView.swift | 1 + iosApp/iosApp/Residence/ResidenceDetailView.swift | 5 +++++ 7 files changed, 33 insertions(+), 20 deletions(-) diff --git a/composeApp/src/commonMain/kotlin/com/example/mycrib/models/Contractor.kt b/composeApp/src/commonMain/kotlin/com/example/mycrib/models/Contractor.kt index 46b8e8c..b057b4c 100644 --- a/composeApp/src/commonMain/kotlin/com/example/mycrib/models/Contractor.kt +++ b/composeApp/src/commonMain/kotlin/com/example/mycrib/models/Contractor.kt @@ -8,7 +8,7 @@ data class Contractor( val id: Int, val name: String, val company: String? = null, - val phone: String, + val phone: String? = null, val email: String? = null, @SerialName("secondary_phone") val secondaryPhone: String? = null, val specialty: String? = null, @@ -33,7 +33,7 @@ data class Contractor( data class ContractorCreateRequest( val name: String, val company: String? = null, - val phone: String, + val phone: String? = null, val email: String? = null, @SerialName("secondary_phone") val secondaryPhone: String? = null, val specialty: String? = null, @@ -72,7 +72,7 @@ data class ContractorSummary( val id: Int, val name: String, val company: String? = null, - val phone: String, + val phone: String? = null, val specialty: String? = null, @SerialName("average_rating") val averageRating: Double? = null, @SerialName("is_favorite") val isFavorite: Boolean = false, diff --git a/composeApp/src/commonMain/kotlin/com/example/mycrib/ui/components/AddContractorDialog.kt b/composeApp/src/commonMain/kotlin/com/example/mycrib/ui/components/AddContractorDialog.kt index 8f1d835..67d9e17 100644 --- a/composeApp/src/commonMain/kotlin/com/example/mycrib/ui/components/AddContractorDialog.kt +++ b/composeApp/src/commonMain/kotlin/com/example/mycrib/ui/components/AddContractorDialog.kt @@ -62,7 +62,7 @@ fun AddContractorDialog( val contractor = (contractorDetailState as ApiResult.Success).data name = contractor.name company = contractor.company ?: "" - phone = contractor.phone + phone = contractor.phone ?: "" email = contractor.email ?: "" secondaryPhone = contractor.secondaryPhone ?: "" specialty = contractor.specialty ?: "" @@ -157,7 +157,7 @@ fun AddContractorDialog( OutlinedTextField( value = phone, onValueChange = { phone = it }, - label = { Text("Phone *") }, + label = { Text("Phone") }, modifier = Modifier.fillMaxWidth(), singleLine = true, shape = RoundedCornerShape(12.dp), @@ -402,13 +402,13 @@ fun AddContractorDialog( confirmButton = { Button( onClick = { - if (name.isNotBlank() && phone.isNotBlank()) { + if (name.isNotBlank()) { if (contractorId == null) { viewModel.createContractor( ContractorCreateRequest( name = name, company = company.takeIf { it.isNotBlank() }, - phone = phone, + phone = phone.takeIf { it.isNotBlank() }, email = email.takeIf { it.isNotBlank() }, secondaryPhone = secondaryPhone.takeIf { it.isNotBlank() }, specialty = specialty.takeIf { it.isNotBlank() }, @@ -445,7 +445,7 @@ fun AddContractorDialog( } } }, - enabled = name.isNotBlank() && phone.isNotBlank() && + enabled = name.isNotBlank() && createState !is ApiResult.Loading && updateState !is ApiResult.Loading, colors = ButtonDefaults.buttonColors( containerColor = Color(0xFF2563EB) diff --git a/composeApp/src/commonMain/kotlin/com/example/mycrib/ui/screens/ContractorDetailScreen.kt b/composeApp/src/commonMain/kotlin/com/example/mycrib/ui/screens/ContractorDetailScreen.kt index e35a854..f35e445 100644 --- a/composeApp/src/commonMain/kotlin/com/example/mycrib/ui/screens/ContractorDetailScreen.kt +++ b/composeApp/src/commonMain/kotlin/com/example/mycrib/ui/screens/ContractorDetailScreen.kt @@ -231,12 +231,14 @@ fun ContractorDetailScreen( // Contact Information item { DetailSection(title = "Contact Information") { - DetailRow( - icon = Icons.Default.Phone, - label = "Phone", - value = contractor.phone, - iconTint = Color(0xFF3B82F6) - ) + if (contractor.phone != null) { + DetailRow( + icon = Icons.Default.Phone, + label = "Phone", + value = contractor.phone, + iconTint = Color(0xFF3B82F6) + ) + } if (contractor.email != null) { DetailRow( diff --git a/iosApp/iosApp/Contractor/ContractorDetailView.swift b/iosApp/iosApp/Contractor/ContractorDetailView.swift index 76959fe..ccd1b7b 100644 --- a/iosApp/iosApp/Contractor/ContractorDetailView.swift +++ b/iosApp/iosApp/Contractor/ContractorDetailView.swift @@ -92,7 +92,9 @@ struct ContractorDetailView: View { // Contact Information DetailSection(title: "Contact Information") { - DetailRow(icon: "phone", label: "Phone", value: contractor.phone, iconColor: .blue) + if let phone = contractor.phone { + DetailRow(icon: "phone", label: "Phone", value: phone, iconColor: .blue) + } if let email = contractor.email { DetailRow(icon: "envelope", label: "Email", value: email, iconColor: .purple) diff --git a/iosApp/iosApp/Contractor/ContractorFormSheet.swift b/iosApp/iosApp/Contractor/ContractorFormSheet.swift index 58ce918..9577424 100644 --- a/iosApp/iosApp/Contractor/ContractorFormSheet.swift +++ b/iosApp/iosApp/Contractor/ContractorFormSheet.swift @@ -42,7 +42,7 @@ struct ContractorFormSheet: View { } private var canSave: Bool { - !name.isEmpty && !phone.isEmpty + !name.isEmpty } var body: some View { @@ -67,6 +67,10 @@ struct ContractorFormSheet: View { } } header: { Text("Basic Information") + } footer: { + Text("Required: Name") + .font(.caption) + .foregroundColor(.red) } // Contact Information @@ -102,8 +106,7 @@ struct ContractorFormSheet: View { } header: { Text("Contact Information") } footer: { - Text("Required: Name and Phone") - .font(.caption) + } // Business Details @@ -295,7 +298,7 @@ struct ContractorFormSheet: View { name = contractor.name company = contractor.company ?? "" - phone = contractor.phone + phone = contractor.phone ?? "" email = contractor.email ?? "" secondaryPhone = contractor.secondaryPhone ?? "" specialty = contractor.specialty ?? "" @@ -353,7 +356,7 @@ struct ContractorFormSheet: View { let request = ContractorCreateRequest( name: name, company: company.isEmpty ? nil : company, - phone: phone, + phone: phone.isEmpty ? nil : phone, email: email.isEmpty ? nil : email, secondaryPhone: secondaryPhone.isEmpty ? nil : secondaryPhone, specialty: specialty.isEmpty ? nil : specialty, diff --git a/iosApp/iosApp/Contractor/ContractorsListView.swift b/iosApp/iosApp/Contractor/ContractorsListView.swift index bde111b..aa2bf95 100644 --- a/iosApp/iosApp/Contractor/ContractorsListView.swift +++ b/iosApp/iosApp/Contractor/ContractorsListView.swift @@ -148,6 +148,7 @@ struct ContractorsListView: View { .font(.title2) .foregroundColor(.blue) } + .accessibilityIdentifier(AccessibilityIdentifiers.Contractor.addButton) } } } diff --git a/iosApp/iosApp/Residence/ResidenceDetailView.swift b/iosApp/iosApp/Residence/ResidenceDetailView.swift index 1360da0..c0ba2f0 100644 --- a/iosApp/iosApp/Residence/ResidenceDetailView.swift +++ b/iosApp/iosApp/Residence/ResidenceDetailView.swift @@ -56,9 +56,11 @@ struct ResidenceDetailView: View { .alert("Delete Residence", isPresented: $showDeleteConfirmation) { Button("Cancel", role: .cancel) { } + .accessibilityIdentifier(AccessibilityIdentifiers.Alert.cancelButton) Button("Delete", role: .destructive) { deleteResidence() } + .accessibilityIdentifier(AccessibilityIdentifiers.Alert.deleteButton) } message: { if let residence = viewModel.selectedResidence { Text("Are you sure you want to delete \(residence.name)? This action cannot be undone and will delete all associated tasks, documents, and data.") @@ -223,6 +225,7 @@ private extension ResidenceDetailView { Button("Edit") { showEditResidence = true } + .accessibilityIdentifier(AccessibilityIdentifiers.Residence.editButton) } } } @@ -256,6 +259,7 @@ private extension ResidenceDetailView { } label: { Image(systemName: "plus") } + .accessibilityIdentifier(AccessibilityIdentifiers.Task.addButton) if let residence = viewModel.selectedResidence, residence.isPrimaryOwner { Button { @@ -264,6 +268,7 @@ private extension ResidenceDetailView { Image(systemName: "trash") .foregroundStyle(.red) } + .accessibilityIdentifier(AccessibilityIdentifiers.Residence.deleteButton) } } }