Add centralized DateUtils and enhance contractor detail views

- Add DateUtils.kt for shared Kotlin date formatting with formatDate,
  formatDateMedium, formatDateTime, formatRelativeDate, and isOverdue
- Add DateUtils.swift for iOS with matching date formatting functions
- Enhance ContractorDetailScreen (Android) with quick action buttons
  (call, email, website, directions), clickable contact rows, residence
  association, statistics section, and metadata
- Enhance ContractorDetailView (iOS) with same features, refactored into
  smaller @ViewBuilder functions to fix Swift compiler type-check timeout
- Fix empty string handling in iOS - check !isEmpty in addition to != nil
  for optional fields like phone, email, website, address
- Update various task and document views to use centralized DateUtils

🤖 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-01 14:08:45 -06:00
parent 94781f4c48
commit c07821711f
22 changed files with 1329 additions and 322 deletions

View File

@@ -16,6 +16,7 @@ import com.example.casera.models.TaskCompletionResponse
import com.example.casera.models.TaskCompletion
import com.example.casera.network.ApiResult
import com.example.casera.network.APILayer
import com.example.casera.util.DateUtils
import kotlinx.coroutines.launch
/**
@@ -259,7 +260,7 @@ private fun CompletionHistoryCard(completion: TaskCompletionResponse) {
) {
Column {
Text(
text = formatCompletionDate(completion.completedAt),
text = DateUtils.formatDateMedium(completion.completedAt),
style = MaterialTheme.typography.titleMedium,
fontWeight = FontWeight.Bold,
color = MaterialTheme.colorScheme.onSurface
@@ -377,26 +378,3 @@ private fun CompletionHistoryCard(completion: TaskCompletionResponse) {
)
}
}
private fun formatCompletionDate(dateString: String): String {
// Try to parse and format the date
return try {
val parts = dateString.split("T")
if (parts.isNotEmpty()) {
val dateParts = parts[0].split("-")
if (dateParts.size == 3) {
val year = dateParts[0]
val month = dateParts[1].toIntOrNull() ?: 1
val day = dateParts[2].toIntOrNull() ?: 1
val monthNames = listOf("", "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec")
"${monthNames.getOrElse(month) { "Jan" }} $day, $year"
} else {
dateString
}
} else {
dateString
}
} catch (e: Exception) {
dateString
}
}

View File

@@ -7,6 +7,7 @@ import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
import com.example.casera.util.DateUtils
import org.jetbrains.compose.ui.tooling.preview.Preview
@Composable
@@ -77,7 +78,7 @@ fun SimpleTaskListItem(
)
if (dueDate != null) {
Text(
text = "Due: $dueDate",
text = "Due: ${DateUtils.formatDate(dueDate)}",
style = MaterialTheme.typography.bodySmall,
color = if (isOverdue)
MaterialTheme.colorScheme.error

View File

@@ -20,6 +20,7 @@ import com.example.casera.models.TaskPriority
import com.example.casera.models.TaskFrequency
import com.example.casera.models.TaskStatus
import com.example.casera.models.TaskCompletion
import com.example.casera.util.DateUtils
import org.jetbrains.compose.ui.tooling.preview.Preview
@Composable
@@ -167,7 +168,7 @@ fun TaskCard(
tint = MaterialTheme.colorScheme.onSurfaceVariant
)
Text(
text = task.nextScheduledDate ?: task.dueDate ?: "N/A",
text = DateUtils.formatDate(task.nextScheduledDate ?: task.dueDate) ?: "N/A",
style = MaterialTheme.typography.labelSmall,
color = MaterialTheme.colorScheme.onSurfaceVariant
)