diff --git a/composeApp/src/commonMain/kotlin/com/example/mycrib/App.kt b/composeApp/src/commonMain/kotlin/com/example/mycrib/App.kt index 97c46ab..95ffae9 100644 --- a/composeApp/src/commonMain/kotlin/com/example/mycrib/App.kt +++ b/composeApp/src/commonMain/kotlin/com/example/mycrib/App.kt @@ -286,7 +286,7 @@ fun App( EditResidenceRoute( residenceId = residence.id, name = residence.name, - propertyType = residence.propertyType.toInt(), + propertyType = residence.propertyType?.toInt(), streetAddress = residence.streetAddress, apartmentUnit = residence.apartmentUnit, city = residence.city, @@ -452,7 +452,7 @@ fun App( EditResidenceRoute( residenceId = residence.id, name = residence.name, - propertyType = residence.propertyType.toInt(), + propertyType = residence.propertyType?.toInt(), streetAddress = residence.streetAddress, apartmentUnit = residence.apartmentUnit, city = residence.city, diff --git a/composeApp/src/commonMain/kotlin/com/example/mycrib/models/Residence.kt b/composeApp/src/commonMain/kotlin/com/example/mycrib/models/Residence.kt index 6f147b1..2370417 100644 --- a/composeApp/src/commonMain/kotlin/com/example/mycrib/models/Residence.kt +++ b/composeApp/src/commonMain/kotlin/com/example/mycrib/models/Residence.kt @@ -11,21 +11,21 @@ data class Residence( @SerialName("is_primary_owner") val isPrimaryOwner: Boolean = false, @SerialName("user_count") val userCount: Int = 1, val name: String, - @SerialName("property_type") val propertyType: String, - @SerialName("street_address") val streetAddress: String, - @SerialName("apartment_unit") val apartmentUnit: String?, - val city: String, - @SerialName("state_province") val stateProvince: String, - @SerialName("postal_code") val postalCode: String, - val country: String, - val bedrooms: Int?, - val bathrooms: Float?, - @SerialName("square_footage") val squareFootage: Int?, - @SerialName("lot_size") val lotSize: Float?, - @SerialName("year_built") val yearBuilt: Int?, - val description: String?, - @SerialName("purchase_date") val purchaseDate: String?, - @SerialName("purchase_price") val purchasePrice: Double?, + @SerialName("property_type") val propertyType: String? = null, + @SerialName("street_address") val streetAddress: String? = null, + @SerialName("apartment_unit") val apartmentUnit: String? = null, + val city: String? = null, + @SerialName("state_province") val stateProvince: String? = null, + @SerialName("postal_code") val postalCode: String? = null, + val country: String? = null, + val bedrooms: Int? = null, + val bathrooms: Float? = null, + @SerialName("square_footage") val squareFootage: Int? = null, + @SerialName("lot_size") val lotSize: Float? = null, + @SerialName("year_built") val yearBuilt: Int? = null, + val description: String? = null, + @SerialName("purchase_date") val purchaseDate: String? = null, + @SerialName("purchase_price") val purchasePrice: Double? = null, @SerialName("is_primary") val isPrimary: Boolean = false, @SerialName("created_at") val createdAt: String, @SerialName("updated_at") val updatedAt: String @@ -34,13 +34,13 @@ data class Residence( @Serializable data class ResidenceCreateRequest( val name: String, - @SerialName("property_type") val propertyType: Int, - @SerialName("street_address") val streetAddress: String, + @SerialName("property_type") val propertyType: Int? = null, + @SerialName("street_address") val streetAddress: String? = null, @SerialName("apartment_unit") val apartmentUnit: String? = null, - val city: String, - @SerialName("state_province") val stateProvince: String, - @SerialName("postal_code") val postalCode: String, - val country: String, + val city: String? = null, + @SerialName("state_province") val stateProvince: String? = null, + @SerialName("postal_code") val postalCode: String? = null, + val country: String? = null, val bedrooms: Int? = null, val bathrooms: Float? = null, @SerialName("square_footage") val squareFootage: Int? = null, @@ -80,13 +80,13 @@ data class ResidenceSummary( val owner: Int, @SerialName("owner_username") val ownerUsername: String, val name: String, - @SerialName("property_type") val propertyType: String, - @SerialName("street_address") val streetAddress: String, - @SerialName("apartment_unit") val apartmentUnit: String?, - val city: String, - @SerialName("state_province") val stateProvince: String, - @SerialName("postal_code") val postalCode: String, - val country: String, + @SerialName("property_type") val propertyType: String? = null, + @SerialName("street_address") val streetAddress: String? = null, + @SerialName("apartment_unit") val apartmentUnit: String? = null, + val city: String? = null, + @SerialName("state_province") val stateProvince: String? = null, + @SerialName("postal_code") val postalCode: String? = null, + val country: String? = null, @SerialName("is_primary") val isPrimary: Boolean, @SerialName("task_summary") val taskSummary: TaskSummary, @SerialName("last_completed_task") val lastCompletedCustomTask: CustomTask?, @@ -119,21 +119,21 @@ data class ResidenceWithTasks( @SerialName("is_primary_owner") val isPrimaryOwner: Boolean = false, @SerialName("user_count") val userCount: Int = 1, val name: String, - @SerialName("property_type") val propertyType: String, - @SerialName("street_address") val streetAddress: String, - @SerialName("apartment_unit") val apartmentUnit: String?, - val city: String, - @SerialName("state_province") val stateProvince: String, - @SerialName("postal_code") val postalCode: String, - val country: String, - val bedrooms: Int?, - val bathrooms: Float?, - @SerialName("square_footage") val squareFootage: Int?, - @SerialName("lot_size") val lotSize: Float?, - @SerialName("year_built") val yearBuilt: Int?, - val description: String?, - @SerialName("purchase_date") val purchaseDate: String?, - @SerialName("purchase_price") val purchasePrice: Double?, + @SerialName("property_type") val propertyType: String? = null, + @SerialName("street_address") val streetAddress: String? = null, + @SerialName("apartment_unit") val apartmentUnit: String? = null, + val city: String? = null, + @SerialName("state_province") val stateProvince: String? = null, + @SerialName("postal_code") val postalCode: String? = null, + val country: String? = null, + val bedrooms: Int? = null, + val bathrooms: Float? = null, + @SerialName("square_footage") val squareFootage: Int? = null, + @SerialName("lot_size") val lotSize: Float? = null, + @SerialName("year_built") val yearBuilt: Int? = null, + val description: String? = null, + @SerialName("purchase_date") val purchaseDate: String? = null, + @SerialName("purchase_price") val purchasePrice: Double? = null, @SerialName("is_primary") val isPrimary: Boolean, @SerialName("task_summary") val taskSummary: TaskSummary, val tasks: List, diff --git a/composeApp/src/commonMain/kotlin/com/example/mycrib/navigation/Routes.kt b/composeApp/src/commonMain/kotlin/com/example/mycrib/navigation/Routes.kt index 3b528ee..00252e1 100644 --- a/composeApp/src/commonMain/kotlin/com/example/mycrib/navigation/Routes.kt +++ b/composeApp/src/commonMain/kotlin/com/example/mycrib/navigation/Routes.kt @@ -25,13 +25,13 @@ object AddResidenceRoute data class EditResidenceRoute( val residenceId: Int, val name: String, - val propertyType: Int, - val streetAddress: String, + val propertyType: Int?, + val streetAddress: String?, val apartmentUnit: String?, - val city: String, - val stateProvince: String, - val postalCode: String, - val country: String, + val city: String?, + val stateProvince: String?, + val postalCode: String?, + val country: String?, val bedrooms: Int?, val bathrooms: Float?, val squareFootage: Int?, diff --git a/composeApp/src/commonMain/kotlin/com/example/mycrib/ui/screens/ResidenceDetailScreen.kt b/composeApp/src/commonMain/kotlin/com/example/mycrib/ui/screens/ResidenceDetailScreen.kt index a45be0d..c55e9ed 100644 --- a/composeApp/src/commonMain/kotlin/com/example/mycrib/ui/screens/ResidenceDetailScreen.kt +++ b/composeApp/src/commonMain/kotlin/com/example/mycrib/ui/screens/ResidenceDetailScreen.kt @@ -471,17 +471,27 @@ fun ResidenceDetailScreen( } // Address Card - item { - InfoCard( - icon = Icons.Default.LocationOn, - title = "Address" - ) { - Text(text = residence.streetAddress) - if (residence.apartmentUnit != null) { - Text(text = "Unit: ${residence.apartmentUnit}") + if (residence.streetAddress != null || residence.city != null || + residence.stateProvince != null || residence.postalCode != null || + residence.country != null) { + item { + InfoCard( + icon = Icons.Default.LocationOn, + title = "Address" + ) { + if (residence.streetAddress != null) { + Text(text = residence.streetAddress) + } + if (residence.apartmentUnit != null) { + Text(text = "Unit: ${residence.apartmentUnit}") + } + if (residence.city != null || residence.stateProvince != null || residence.postalCode != null) { + Text(text = "${residence.city ?: ""}, ${residence.stateProvince ?: ""} ${residence.postalCode ?: ""}") + } + if (residence.country != null) { + Text(text = residence.country) + } } - Text(text = "${residence.city}, ${residence.stateProvince} ${residence.postalCode}") - Text(text = residence.country) } } diff --git a/composeApp/src/commonMain/kotlin/com/example/mycrib/ui/screens/ResidenceFormScreen.kt b/composeApp/src/commonMain/kotlin/com/example/mycrib/ui/screens/ResidenceFormScreen.kt index 0b4d645..9a79ae1 100644 --- a/composeApp/src/commonMain/kotlin/com/example/mycrib/ui/screens/ResidenceFormScreen.kt +++ b/composeApp/src/commonMain/kotlin/com/example/mycrib/ui/screens/ResidenceFormScreen.kt @@ -56,10 +56,6 @@ fun ResidenceFormScreen( // Validation errors var nameError by remember { mutableStateOf("") } - var streetAddressError by remember { mutableStateOf("") } - var cityError by remember { mutableStateOf("") } - var stateProvinceError by remember { mutableStateOf("") } - var postalCodeError by remember { mutableStateOf("") } // Handle operation state changes LaunchedEffect(operationState) { @@ -79,10 +75,12 @@ fun ResidenceFormScreen( // Set default/existing property type when types are loaded LaunchedEffect(propertyTypes, existingResidence) { if (propertyTypes.isNotEmpty() && propertyType == null) { - propertyType = if (isEditMode && existingResidence != null) { + propertyType = if (isEditMode && existingResidence != null && existingResidence.propertyType != null) { propertyTypes.find { it.id == existingResidence.propertyType.toInt() } - } else { + } else if (!isEditMode && propertyTypes.isNotEmpty()) { propertyTypes.first() + } else { + null } } } @@ -97,34 +95,6 @@ fun ResidenceFormScreen( nameError = "" } - if (streetAddress.isBlank()) { - streetAddressError = "Street address is required" - isValid = false - } else { - streetAddressError = "" - } - - if (city.isBlank()) { - cityError = "City is required" - isValid = false - } else { - cityError = "" - } - - if (stateProvince.isBlank()) { - stateProvinceError = "State/Province is required" - isValid = false - } else { - stateProvinceError = "" - } - - if (postalCode.isBlank()) { - postalCodeError = "Postal code is required" - isValid = false - } else { - postalCodeError = "" - } - return isValid } @@ -148,9 +118,9 @@ fun ResidenceFormScreen( .verticalScroll(rememberScrollState()), verticalArrangement = Arrangement.spacedBy(16.dp) ) { - // Required fields section + // Basic Information section Text( - text = "Required Information", + text = "Property Details", style = MaterialTheme.typography.titleMedium, color = MaterialTheme.colorScheme.primary ) @@ -162,8 +132,10 @@ fun ResidenceFormScreen( modifier = Modifier.fillMaxWidth(), isError = nameError.isNotEmpty(), supportingText = if (nameError.isNotEmpty()) { - { Text(nameError) } - } else null + { Text(nameError, color = MaterialTheme.colorScheme.error) } + } else { + { Text("Required", color = MaterialTheme.colorScheme.error) } + } ) ExposedDropdownMenuBox( @@ -174,7 +146,7 @@ fun ResidenceFormScreen( value = propertyType?.name?.replaceFirstChar { it.uppercase() } ?: "", onValueChange = {}, readOnly = true, - label = { Text("Property Type *") }, + label = { Text("Property Type") }, trailingIcon = { ExposedDropdownMenuDefaults.TrailingIcon(expanded = expanded) }, modifier = Modifier .fillMaxWidth() @@ -197,15 +169,18 @@ fun ResidenceFormScreen( } } + // Address section + Text( + text = "Address", + style = MaterialTheme.typography.titleMedium, + color = MaterialTheme.colorScheme.primary + ) + OutlinedTextField( value = streetAddress, onValueChange = { streetAddress = it }, - label = { Text("Street Address *") }, - modifier = Modifier.fillMaxWidth(), - isError = streetAddressError.isNotEmpty(), - supportingText = if (streetAddressError.isNotEmpty()) { - { Text(streetAddressError) } - } else null + label = { Text("Street Address") }, + modifier = Modifier.fillMaxWidth() ) OutlinedTextField( @@ -218,34 +193,22 @@ fun ResidenceFormScreen( OutlinedTextField( value = city, onValueChange = { city = it }, - label = { Text("City *") }, - modifier = Modifier.fillMaxWidth(), - isError = cityError.isNotEmpty(), - supportingText = if (cityError.isNotEmpty()) { - { Text(cityError) } - } else null + label = { Text("City") }, + modifier = Modifier.fillMaxWidth() ) OutlinedTextField( value = stateProvince, onValueChange = { stateProvince = it }, - label = { Text("State/Province *") }, - modifier = Modifier.fillMaxWidth(), - isError = stateProvinceError.isNotEmpty(), - supportingText = if (stateProvinceError.isNotEmpty()) { - { Text(stateProvinceError) } - } else null + label = { Text("State/Province") }, + modifier = Modifier.fillMaxWidth() ) OutlinedTextField( value = postalCode, onValueChange = { postalCode = it }, - label = { Text("Postal Code *") }, - modifier = Modifier.fillMaxWidth(), - isError = postalCodeError.isNotEmpty(), - supportingText = if (postalCodeError.isNotEmpty()) { - { Text(postalCodeError) } - } else null + label = { Text("Postal Code") }, + modifier = Modifier.fillMaxWidth() ) OutlinedTextField( @@ -340,16 +303,16 @@ fun ResidenceFormScreen( // Submit button Button( onClick = { - if (validateForm() && propertyType != null) { + if (validateForm()) { val request = ResidenceCreateRequest( name = name, - propertyType = propertyType!!.id, - streetAddress = streetAddress, + propertyType = propertyType?.id, + streetAddress = streetAddress.ifBlank { null }, apartmentUnit = apartmentUnit.ifBlank { null }, - city = city, - stateProvince = stateProvince, - postalCode = postalCode, - country = country, + city = city.ifBlank { null }, + stateProvince = stateProvince.ifBlank { null }, + postalCode = postalCode.ifBlank { null }, + country = country.ifBlank { null }, bedrooms = bedrooms.toIntOrNull(), bathrooms = bathrooms.toFloatOrNull(), squareFootage = squareFootage.toIntOrNull(), @@ -367,7 +330,7 @@ fun ResidenceFormScreen( } }, modifier = Modifier.fillMaxWidth(), - enabled = validateForm() && propertyType != null + enabled = validateForm() ) { if (operationState is ApiResult.Loading) { CircularProgressIndicator( diff --git a/iosApp/iosApp/ResidenceFormView.swift b/iosApp/iosApp/ResidenceFormView.swift index 1dd614f..3dd7a3c 100644 --- a/iosApp/iosApp/ResidenceFormView.swift +++ b/iosApp/iosApp/ResidenceFormView.swift @@ -30,10 +30,6 @@ struct ResidenceFormView: View { // Validation errors @State private var nameError: String = "" - @State private var streetAddressError: String = "" - @State private var cityError: String = "" - @State private var stateProvinceError: String = "" - @State private var postalCodeError: String = "" enum Field { case name, streetAddress, apartmentUnit, city, stateProvince, postalCode, country @@ -44,12 +40,17 @@ struct ResidenceFormView: View { existingResidence != nil } + private var canSave: Bool { + !name.isEmpty + } + var body: some View { NavigationView { Form { - Section(header: Text("Property Details")) { + Section { TextField("Property Name", text: $name) .focused($focusedField, equals: .name) + .accessibilityIdentifier(AccessibilityIdentifiers.Residence.nameField) if !nameError.isEmpty { Text(nameError) @@ -63,50 +64,41 @@ struct ResidenceFormView: View { Text(type.name).tag(type as ResidenceType?) } } + .accessibilityIdentifier(AccessibilityIdentifiers.Residence.propertyTypePicker) + } header: { + Text("Property Details") + } footer: { + Text("Required: Name") + .font(.caption) + .foregroundColor(.red) } - Section(header: Text("Address")) { + Section { TextField("Street Address", text: $streetAddress) .focused($focusedField, equals: .streetAddress) - - if !streetAddressError.isEmpty { - Text(streetAddressError) - .font(.caption) - .foregroundColor(.red) - } + .accessibilityIdentifier(AccessibilityIdentifiers.Residence.streetAddressField) TextField("Apartment/Unit (optional)", text: $apartmentUnit) .focused($focusedField, equals: .apartmentUnit) + .accessibilityIdentifier(AccessibilityIdentifiers.Residence.apartmentUnitField) TextField("City", text: $city) .focused($focusedField, equals: .city) - - if !cityError.isEmpty { - Text(cityError) - .font(.caption) - .foregroundColor(.red) - } + .accessibilityIdentifier(AccessibilityIdentifiers.Residence.cityField) TextField("State/Province", text: $stateProvince) .focused($focusedField, equals: .stateProvince) - - if !stateProvinceError.isEmpty { - Text(stateProvinceError) - .font(.caption) - .foregroundColor(.red) - } + .accessibilityIdentifier(AccessibilityIdentifiers.Residence.stateProvinceField) TextField("Postal Code", text: $postalCode) .focused($focusedField, equals: .postalCode) - - if !postalCodeError.isEmpty { - Text(postalCodeError) - .font(.caption) - .foregroundColor(.red) - } + .accessibilityIdentifier(AccessibilityIdentifiers.Residence.postalCodeField) TextField("Country", text: $country) .focused($focusedField, equals: .country) + .accessibilityIdentifier(AccessibilityIdentifiers.Residence.countryField) + } header: { + Text("Address") } Section(header: Text("Property Features")) { @@ -118,6 +110,7 @@ struct ResidenceFormView: View { .multilineTextAlignment(.trailing) .frame(width: 60) .focused($focusedField, equals: .bedrooms) + .accessibilityIdentifier(AccessibilityIdentifiers.Residence.bedroomsField) } HStack { @@ -128,26 +121,32 @@ struct ResidenceFormView: View { .multilineTextAlignment(.trailing) .frame(width: 60) .focused($focusedField, equals: .bathrooms) + .accessibilityIdentifier(AccessibilityIdentifiers.Residence.bathroomsField) } TextField("Square Footage", text: $squareFootage) .keyboardType(.numberPad) .focused($focusedField, equals: .squareFootage) + .accessibilityIdentifier(AccessibilityIdentifiers.Residence.squareFootageField) TextField("Lot Size (acres)", text: $lotSize) .keyboardType(.decimalPad) .focused($focusedField, equals: .lotSize) + .accessibilityIdentifier(AccessibilityIdentifiers.Residence.lotSizeField) TextField("Year Built", text: $yearBuilt) .keyboardType(.numberPad) .focused($focusedField, equals: .yearBuilt) + .accessibilityIdentifier(AccessibilityIdentifiers.Residence.yearBuiltField) } Section(header: Text("Additional Details")) { TextField("Description (optional)", text: $description, axis: .vertical) .lineLimit(3...6) + .accessibilityIdentifier(AccessibilityIdentifiers.Residence.descriptionField) Toggle("Primary Residence", isOn: $isPrimary) + .accessibilityIdentifier(AccessibilityIdentifiers.Residence.isPrimaryToggle) } if let errorMessage = viewModel.errorMessage { @@ -165,13 +164,15 @@ struct ResidenceFormView: View { Button("Cancel") { isPresented = false } + .accessibilityIdentifier(AccessibilityIdentifiers.Residence.formCancelButton) } ToolbarItem(placement: .navigationBarTrailing) { Button("Save") { submitForm() } - .disabled(viewModel.isLoading) + .disabled(!canSave || viewModel.isLoading) + .accessibilityIdentifier(AccessibilityIdentifiers.Residence.saveButton) } } .onAppear { @@ -197,7 +198,9 @@ struct ResidenceFormView: View { } else { // Fallback to DataCache directly await MainActor.run { - self.residenceTypes = DataCache.shared.residenceTypes.value as! [ResidenceType] + if let cached = DataCache.shared.residenceTypes.value as? [ResidenceType] { + self.residenceTypes = cached + } } } } @@ -207,12 +210,12 @@ struct ResidenceFormView: View { if let residence = existingResidence { // Edit mode - populate fields from existing residence name = residence.name - streetAddress = residence.streetAddress + streetAddress = residence.streetAddress ?? "" apartmentUnit = residence.apartmentUnit ?? "" - city = residence.city - stateProvince = residence.stateProvince - postalCode = residence.postalCode - country = residence.country + city = residence.city ?? "" + stateProvince = residence.stateProvince ?? "" + postalCode = residence.postalCode ?? "" + country = residence.country ?? "" bedrooms = residence.bedrooms != nil ? "\(residence.bedrooms!)" : "" bathrooms = residence.bathrooms != nil ? "\(residence.bathrooms!)" : "" squareFootage = residence.squareFootage != nil ? "\(residence.squareFootage!)" : "" @@ -222,13 +225,11 @@ struct ResidenceFormView: View { isPrimary = residence.isPrimary // Set the selected property type - selectedPropertyType = residenceTypes.first { $0.id == Int(residence.propertyType) ?? 0 } - } else { - // Add mode - set default property type - if selectedPropertyType == nil && !residenceTypes.isEmpty { - selectedPropertyType = residenceTypes.first + if let propertyTypeStr = residence.propertyType, let propertyTypeId = Int(propertyTypeStr) { + selectedPropertyType = residenceTypes.first { $0.id == propertyTypeId } } } + // In add mode, leave selectedPropertyType as nil to force user to select } private func validateForm() -> Bool { @@ -241,57 +242,54 @@ struct ResidenceFormView: View { nameError = "" } - if streetAddress.isEmpty { - streetAddressError = "Street address is required" - isValid = false - } else { - streetAddressError = "" - } - - if city.isEmpty { - cityError = "City is required" - isValid = false - } else { - cityError = "" - } - - if stateProvince.isEmpty { - stateProvinceError = "State/Province is required" - isValid = false - } else { - stateProvinceError = "" - } - - if postalCode.isEmpty { - postalCodeError = "Postal code is required" - isValid = false - } else { - postalCodeError = "" - } - return isValid } private func submitForm() { guard validateForm() else { return } - guard let propertyType = selectedPropertyType else { - return - } + + // Convert optional numeric fields to Kotlin types + let bedroomsValue: KotlinInt? = { + guard !bedrooms.isEmpty, let value = Int32(bedrooms) else { return nil } + return KotlinInt(int: value) + }() + let bathroomsValue: KotlinFloat? = { + guard !bathrooms.isEmpty, let value = Float(bathrooms) else { return nil } + return KotlinFloat(float: value) + }() + let squareFootageValue: KotlinInt? = { + guard !squareFootage.isEmpty, let value = Int32(squareFootage) else { return nil } + return KotlinInt(int: value) + }() + let lotSizeValue: KotlinFloat? = { + guard !lotSize.isEmpty, let value = Float(lotSize) else { return nil } + return KotlinFloat(float: value) + }() + let yearBuiltValue: KotlinInt? = { + guard !yearBuilt.isEmpty, let value = Int32(yearBuilt) else { return nil } + return KotlinInt(int: value) + }() + + // Convert propertyType to KotlinInt if it exists + let propertyTypeValue: KotlinInt? = { + guard let type = selectedPropertyType else { return nil } + return KotlinInt(int: Int32(type.id)) + }() let request = ResidenceCreateRequest( name: name, - propertyType: Int32(propertyType.id), - streetAddress: streetAddress, + propertyType: propertyTypeValue, + streetAddress: streetAddress.isEmpty ? nil : streetAddress, apartmentUnit: apartmentUnit.isEmpty ? nil : apartmentUnit, - city: city, - stateProvince: stateProvince, - postalCode: postalCode, - country: country, - bedrooms: Int32(bedrooms) as? KotlinInt, - bathrooms: Float(bathrooms) as? KotlinFloat, - squareFootage: Int32(squareFootage) as? KotlinInt, - lotSize: Float(lotSize) as? KotlinFloat, - yearBuilt: Int32(yearBuilt) as? KotlinInt, + city: city.isEmpty ? nil : city, + stateProvince: stateProvince.isEmpty ? nil : stateProvince, + postalCode: postalCode.isEmpty ? nil : postalCode, + country: country.isEmpty ? nil : country, + bedrooms: bedroomsValue, + bathrooms: bathroomsValue, + squareFootage: squareFootageValue, + lotSize: lotSizeValue, + yearBuilt: yearBuiltValue, description: description.isEmpty ? nil : description, purchaseDate: nil, purchasePrice: nil, diff --git a/iosApp/iosApp/Subviews/Residence/PropertyHeaderCard.swift b/iosApp/iosApp/Subviews/Residence/PropertyHeaderCard.swift index 6980504..b250407 100644 --- a/iosApp/iosApp/Subviews/Residence/PropertyHeaderCard.swift +++ b/iosApp/iosApp/Subviews/Residence/PropertyHeaderCard.swift @@ -16,9 +16,11 @@ struct PropertyHeaderCard: View { .font(.title2) .fontWeight(.bold) - Text(residence.propertyType) - .font(.caption) - .foregroundColor(.secondary) + if let propertyType = residence.propertyType { + Text(propertyType) + .font(.caption) + .foregroundColor(.secondary) + } } Spacer() @@ -27,15 +29,19 @@ struct PropertyHeaderCard: View { Divider() VStack(alignment: .leading, spacing: 4) { - Label(residence.streetAddress, systemImage: "mappin.circle.fill") - .font(.subheadline) + if let streetAddress = residence.streetAddress { + Label(streetAddress, systemImage: "mappin.circle.fill") + .font(.subheadline) + } - Text("\(residence.city), \(residence.stateProvince) \(residence.postalCode)") - .font(.subheadline) - .foregroundColor(.secondary) + if residence.city != nil || residence.stateProvince != nil || residence.postalCode != nil { + Text("\(residence.city ?? ""), \(residence.stateProvince ?? "") \(residence.postalCode ?? "")") + .font(.subheadline) + .foregroundColor(.secondary) + } - if !residence.country.isEmpty { - Text(residence.country) + if let country = residence.country, !country.isEmpty { + Text(country) .font(.caption) .foregroundColor(.secondary) } diff --git a/iosApp/iosApp/Subviews/Residence/ResidenceCard.swift b/iosApp/iosApp/Subviews/Residence/ResidenceCard.swift index 30ee416..ee16808 100644 --- a/iosApp/iosApp/Subviews/Residence/ResidenceCard.swift +++ b/iosApp/iosApp/Subviews/Residence/ResidenceCard.swift @@ -26,11 +26,13 @@ struct ResidenceCard: View { .foregroundColor(Color(.label)) .lineLimit(1) - Text(residence.propertyType) - .font(.caption.weight(.medium)) - .foregroundColor(Color(.tertiaryLabel)) - .textCase(.uppercase) - .tracking(0.5) + if let propertyType = residence.propertyType { + Text(propertyType) + .font(.caption.weight(.medium)) + .foregroundColor(Color(.tertiaryLabel)) + .textCase(.uppercase) + .tracking(0.5) + } } Spacer() @@ -49,22 +51,26 @@ struct ResidenceCard: View { // Address VStack(alignment: .leading, spacing: AppSpacing.xxs) { - HStack(spacing: AppSpacing.xxs) { - Image(systemName: "mappin.circle.fill") - .font(.system(size: 12, weight: .medium)) - .foregroundColor(Color(.tertiaryLabel)) - Text(residence.streetAddress) - .font(.callout) - .foregroundColor(Color(.secondaryLabel)) + if let streetAddress = residence.streetAddress { + HStack(spacing: AppSpacing.xxs) { + Image(systemName: "mappin.circle.fill") + .font(.system(size: 12, weight: .medium)) + .foregroundColor(Color(.tertiaryLabel)) + Text(streetAddress) + .font(.callout) + .foregroundColor(Color(.secondaryLabel)) + } } - HStack(spacing: AppSpacing.xxs) { - Image(systemName: "location.fill") - .font(.system(size: 12, weight: .medium)) - .foregroundColor(Color(.tertiaryLabel)) - Text("\(residence.city), \(residence.stateProvince)") - .font(.callout) - .foregroundColor(Color(.secondaryLabel)) + if residence.city != nil || residence.stateProvince != nil { + HStack(spacing: AppSpacing.xxs) { + Image(systemName: "location.fill") + .font(.system(size: 12, weight: .medium)) + .foregroundColor(Color(.tertiaryLabel)) + Text("\(residence.city ?? ""), \(residence.stateProvince ?? "")") + .font(.callout) + .foregroundColor(Color(.secondaryLabel)) + } } } .padding(.vertical, AppSpacing.xs)