- Add overdueCount field to ResidenceResponse model - ResidenceCard shows PulsingIconView when residence has overdue tasks - SummaryCard uses static CaseraIconView (never pulses) - APILayer refreshes residence data after task completion to update overdue counts and animation state 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
253 lines
8.1 KiB
Kotlin
253 lines
8.1 KiB
Kotlin
package com.example.casera.models
|
|
|
|
import kotlinx.serialization.SerialName
|
|
import kotlinx.serialization.Serializable
|
|
|
|
/**
|
|
* User reference for residence responses - matching Go API ResidenceUserResponse
|
|
*/
|
|
@Serializable
|
|
data class ResidenceUserResponse(
|
|
val id: Int,
|
|
val username: String,
|
|
val email: String,
|
|
@SerialName("first_name") val firstName: String = "",
|
|
@SerialName("last_name") val lastName: String = ""
|
|
) {
|
|
val displayName: String
|
|
get() = when {
|
|
firstName.isNotBlank() && lastName.isNotBlank() -> "$firstName $lastName"
|
|
firstName.isNotBlank() -> firstName
|
|
lastName.isNotBlank() -> lastName
|
|
else -> username
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Residence response matching Go API ResidenceResponse
|
|
*/
|
|
@Serializable
|
|
data class ResidenceResponse(
|
|
val id: Int,
|
|
@SerialName("owner_id") val ownerId: Int,
|
|
val owner: ResidenceUserResponse? = null,
|
|
val users: List<ResidenceUserResponse> = emptyList(),
|
|
val name: String,
|
|
@SerialName("property_type_id") val propertyTypeId: Int? = null,
|
|
@SerialName("property_type") val propertyType: ResidenceType? = null,
|
|
@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? = null,
|
|
val bathrooms: Double? = null,
|
|
@SerialName("square_footage") val squareFootage: Int? = null,
|
|
@SerialName("lot_size") val lotSize: Double? = null,
|
|
@SerialName("year_built") val yearBuilt: Int? = null,
|
|
val description: String = "",
|
|
@SerialName("purchase_date") val purchaseDate: String? = null,
|
|
@SerialName("purchase_price") val purchasePrice: Double? = null,
|
|
@SerialName("is_primary") val isPrimary: Boolean = false,
|
|
@SerialName("is_active") val isActive: Boolean = true,
|
|
@SerialName("overdue_count") val overdueCount: Int = 0,
|
|
@SerialName("created_at") val createdAt: String,
|
|
@SerialName("updated_at") val updatedAt: String
|
|
) {
|
|
// Helper to get owner username
|
|
val ownerUsername: String get() = owner?.displayName ?: ""
|
|
|
|
// Helper to get property type name
|
|
val propertyTypeName: String? get() = propertyType?.name
|
|
|
|
// Backwards compatibility for UI code
|
|
// Note: isPrimaryOwner requires comparing with current user - can't be computed here
|
|
// UI components should check ownerId == currentUserId instead
|
|
|
|
// Stub task summary for UI compatibility (Go API doesn't return this per-residence)
|
|
val taskSummary: ResidenceTaskSummary get() = ResidenceTaskSummary()
|
|
|
|
// Stub summary for UI compatibility
|
|
val summary: ResidenceSummaryResponse get() = ResidenceSummaryResponse(id = id, name = name)
|
|
}
|
|
|
|
/**
|
|
* Residence create request matching Go API CreateResidenceRequest
|
|
*/
|
|
@Serializable
|
|
data class ResidenceCreateRequest(
|
|
val name: String,
|
|
@SerialName("property_type_id") val propertyTypeId: Int? = 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: Double? = null,
|
|
@SerialName("square_footage") val squareFootage: Int? = null,
|
|
@SerialName("lot_size") val lotSize: Double? = 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? = null
|
|
)
|
|
|
|
/**
|
|
* Residence update request matching Go API UpdateResidenceRequest
|
|
*/
|
|
@Serializable
|
|
data class ResidenceUpdateRequest(
|
|
val name: String? = null,
|
|
@SerialName("property_type_id") val propertyTypeId: Int? = 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: Double? = null,
|
|
@SerialName("square_footage") val squareFootage: Int? = null,
|
|
@SerialName("lot_size") val lotSize: Double? = 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? = null
|
|
)
|
|
|
|
/**
|
|
* Share code response matching Go API ShareCodeResponse
|
|
*/
|
|
@Serializable
|
|
data class ShareCodeResponse(
|
|
val id: Int,
|
|
val code: String,
|
|
@SerialName("residence_id") val residenceId: Int,
|
|
@SerialName("created_by_id") val createdById: Int,
|
|
@SerialName("is_active") val isActive: Boolean,
|
|
@SerialName("expires_at") val expiresAt: String?,
|
|
@SerialName("created_at") val createdAt: String
|
|
)
|
|
|
|
/**
|
|
* Generate share code request
|
|
*/
|
|
@Serializable
|
|
data class GenerateShareCodeRequest(
|
|
@SerialName("expires_in_hours") val expiresInHours: Int? = null
|
|
)
|
|
|
|
/**
|
|
* Generate share code response matching Go API GenerateShareCodeResponse
|
|
*/
|
|
@Serializable
|
|
data class GenerateShareCodeResponse(
|
|
val message: String,
|
|
@SerialName("share_code") val shareCode: ShareCodeResponse
|
|
)
|
|
|
|
/**
|
|
* Join residence request
|
|
*/
|
|
@Serializable
|
|
data class JoinResidenceRequest(
|
|
val code: String
|
|
)
|
|
|
|
/**
|
|
* Join residence response matching Go API JoinResidenceResponse
|
|
*/
|
|
@Serializable
|
|
data class JoinResidenceResponse(
|
|
val message: String,
|
|
val residence: ResidenceResponse
|
|
)
|
|
|
|
/**
|
|
* Total summary for dashboard display
|
|
*/
|
|
@Serializable
|
|
data class TotalSummary(
|
|
@SerialName("total_residences") val totalResidences: Int = 0,
|
|
@SerialName("total_tasks") val totalTasks: Int = 0,
|
|
@SerialName("total_pending") val totalPending: Int = 0,
|
|
@SerialName("total_overdue") val totalOverdue: Int = 0,
|
|
@SerialName("tasks_due_next_week") val tasksDueNextWeek: Int = 0,
|
|
@SerialName("tasks_due_next_month") val tasksDueNextMonth: Int = 0
|
|
)
|
|
|
|
/**
|
|
* My residences response - list of user's residences
|
|
* Go API returns array directly, this wraps for consistency
|
|
*/
|
|
@Serializable
|
|
data class MyResidencesResponse(
|
|
val residences: List<ResidenceResponse>,
|
|
val summary: TotalSummary = TotalSummary()
|
|
)
|
|
|
|
/**
|
|
* Residence summary response for dashboard
|
|
*/
|
|
@Serializable
|
|
data class ResidenceSummaryResponse(
|
|
val id: Int = 0,
|
|
val name: String = "",
|
|
@SerialName("task_count") val taskCount: Int = 0,
|
|
@SerialName("pending_count") val pendingCount: Int = 0,
|
|
@SerialName("overdue_count") val overdueCount: Int = 0
|
|
)
|
|
|
|
/**
|
|
* Task category summary for residence
|
|
*/
|
|
@Serializable
|
|
data class TaskCategorySummary(
|
|
val name: String,
|
|
@SerialName("display_name") val displayName: String,
|
|
val icons: TaskCategoryIcons,
|
|
val color: String,
|
|
val count: Int
|
|
)
|
|
|
|
/**
|
|
* Icons for task category (Android/iOS)
|
|
*/
|
|
@Serializable
|
|
data class TaskCategoryIcons(
|
|
val android: String = "",
|
|
val ios: String = ""
|
|
)
|
|
|
|
/**
|
|
* Task summary per residence (for UI backwards compatibility)
|
|
*/
|
|
@Serializable
|
|
data class ResidenceTaskSummary(
|
|
val categories: List<TaskCategorySummary> = emptyList()
|
|
)
|
|
|
|
/**
|
|
* Residence users response - API returns a flat list of all users with access
|
|
*/
|
|
typealias ResidenceUsersResponse = List<ResidenceUserResponse>
|
|
|
|
/**
|
|
* Remove user response
|
|
*/
|
|
@Serializable
|
|
data class RemoveUserResponse(
|
|
val message: String
|
|
)
|
|
|
|
// Type aliases for backwards compatibility with existing code
|
|
typealias Residence = ResidenceResponse
|
|
typealias ResidenceShareCode = ShareCodeResponse
|
|
typealias ResidenceUser = ResidenceUserResponse
|
|
typealias TaskSummary = ResidenceTaskSummary
|
|
typealias TaskColumnCategory = TaskCategorySummary |