diff --git a/composeApp/src/commonMain/kotlin/com/example/mycrib/ui/components/auth/AuthHeader.kt b/composeApp/src/commonMain/kotlin/com/example/mycrib/ui/components/auth/AuthHeader.kt new file mode 100644 index 0000000..b0a87f6 --- /dev/null +++ b/composeApp/src/commonMain/kotlin/com/example/mycrib/ui/components/auth/AuthHeader.kt @@ -0,0 +1,62 @@ +package com.mycrib.android.ui.components.auth + +import androidx.compose.foundation.layout.* +import androidx.compose.material3.* +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.vector.ImageVector +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.unit.dp +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Home +import org.jetbrains.compose.ui.tooling.preview.Preview + +@Composable +fun AuthHeader( + icon: ImageVector, + title: String, + subtitle: String, + modifier: Modifier = Modifier +) { + Column( + modifier = modifier, + horizontalAlignment = Alignment.CenterHorizontally, + verticalArrangement = Arrangement.spacedBy(12.dp) + ) { + Icon( + icon, + contentDescription = null, + modifier = Modifier.size(64.dp), + tint = MaterialTheme.colorScheme.primary + ) + + Text( + text = title, + style = MaterialTheme.typography.displaySmall, + fontWeight = FontWeight.Bold, + color = MaterialTheme.colorScheme.primary + ) + + Text( + text = subtitle, + style = MaterialTheme.typography.bodyMedium, + color = MaterialTheme.colorScheme.onSurfaceVariant + ) + } +} + +@Preview +@Composable +fun AuthHeaderPreview() { + MaterialTheme { + Surface { + AuthHeader( + icon = Icons.Default.Home, + title = "myCrib", + subtitle = "Manage your properties with ease", + modifier = Modifier.padding(32.dp) + ) + } + } +} diff --git a/composeApp/src/commonMain/kotlin/com/example/mycrib/ui/components/common/ErrorCard.kt b/composeApp/src/commonMain/kotlin/com/example/mycrib/ui/components/common/ErrorCard.kt new file mode 100644 index 0000000..2213a77 --- /dev/null +++ b/composeApp/src/commonMain/kotlin/com/example/mycrib/ui/components/common/ErrorCard.kt @@ -0,0 +1,43 @@ +package com.mycrib.android.ui.components.common + +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material3.* +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.dp +import org.jetbrains.compose.ui.tooling.preview.Preview + +@Composable +fun ErrorCard( + message: String, + modifier: Modifier = Modifier +) { + if (message.isNotEmpty()) { + Card( + colors = CardDefaults.cardColors( + containerColor = MaterialTheme.colorScheme.errorContainer + ), + modifier = modifier.fillMaxWidth(), + shape = RoundedCornerShape(12.dp) + ) { + Text( + text = message, + color = MaterialTheme.colorScheme.onErrorContainer, + modifier = Modifier.padding(16.dp), + style = MaterialTheme.typography.bodyMedium + ) + } + } +} + +@Preview +@Composable +fun ErrorCardPreview() { + MaterialTheme { + ErrorCard( + message = "Invalid username or password. Please try again.", + modifier = Modifier.padding(16.dp) + ) + } +} diff --git a/composeApp/src/commonMain/kotlin/com/example/mycrib/ui/components/common/InfoCard.kt b/composeApp/src/commonMain/kotlin/com/example/mycrib/ui/components/common/InfoCard.kt new file mode 100644 index 0000000..d971b58 --- /dev/null +++ b/composeApp/src/commonMain/kotlin/com/example/mycrib/ui/components/common/InfoCard.kt @@ -0,0 +1,69 @@ +package com.mycrib.android.ui.components.common + +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material3.* +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.vector.ImageVector +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.unit.dp +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Info +import org.jetbrains.compose.ui.tooling.preview.Preview + +@Composable +fun InfoCard( + icon: ImageVector, + title: String, + content: @Composable ColumnScope.() -> Unit +) { + Card( + modifier = Modifier.fillMaxWidth(), + shape = RoundedCornerShape(16.dp), + elevation = CardDefaults.cardElevation(defaultElevation = 2.dp) + ) { + Column( + modifier = Modifier + .fillMaxWidth() + .padding(20.dp), + verticalArrangement = Arrangement.spacedBy(8.dp) + ) { + Row( + verticalAlignment = Alignment.CenterVertically + ) { + Icon( + icon, + contentDescription = null, + tint = MaterialTheme.colorScheme.primary, + modifier = Modifier.size(24.dp) + ) + Spacer(modifier = Modifier.width(8.dp)) + Text( + text = title, + style = MaterialTheme.typography.titleMedium, + fontWeight = FontWeight.Bold, + color = MaterialTheme.colorScheme.primary + ) + } + Spacer(modifier = Modifier.height(4.dp)) + content() + } + } +} + +@Preview +@Composable +fun InfoCardPreview() { + MaterialTheme { + InfoCard( + icon = Icons.Default.Info, + title = "Sample Information" + ) { + Text("This is sample content") + Text("Line 2 of content") + Text("Line 3 of content") + } + } +} diff --git a/composeApp/src/commonMain/kotlin/com/example/mycrib/ui/components/common/StatItem.kt b/composeApp/src/commonMain/kotlin/com/example/mycrib/ui/components/common/StatItem.kt new file mode 100644 index 0000000..75e9d89 --- /dev/null +++ b/composeApp/src/commonMain/kotlin/com/example/mycrib/ui/components/common/StatItem.kt @@ -0,0 +1,60 @@ +package com.mycrib.android.ui.components.common + +import androidx.compose.foundation.layout.* +import androidx.compose.material3.* +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.vector.ImageVector +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.unit.dp +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Home +import org.jetbrains.compose.ui.tooling.preview.Preview + +@Composable +fun StatItem( + icon: ImageVector, + value: String, + label: String +) { + Column( + horizontalAlignment = Alignment.CenterHorizontally, + verticalArrangement = Arrangement.spacedBy(8.dp) + ) { + Icon( + icon, + contentDescription = null, + modifier = Modifier.size(28.dp), + tint = MaterialTheme.colorScheme.onPrimaryContainer + ) + Text( + text = value, + style = MaterialTheme.typography.headlineMedium, + fontWeight = FontWeight.Bold, + color = MaterialTheme.colorScheme.onPrimaryContainer + ) + Text( + text = label, + style = MaterialTheme.typography.bodySmall, + color = MaterialTheme.colorScheme.onPrimaryContainer.copy(alpha = 0.8f) + ) + } +} + +@Preview +@Composable +fun StatItemPreview() { + MaterialTheme { + Surface( + color = MaterialTheme.colorScheme.primaryContainer, + modifier = Modifier.padding(16.dp) + ) { + StatItem( + icon = Icons.Default.Home, + value = "5", + label = "Properties" + ) + } + } +} diff --git a/composeApp/src/commonMain/kotlin/com/example/mycrib/ui/components/residence/DetailRow.kt b/composeApp/src/commonMain/kotlin/com/example/mycrib/ui/components/residence/DetailRow.kt new file mode 100644 index 0000000..a6d1312 --- /dev/null +++ b/composeApp/src/commonMain/kotlin/com/example/mycrib/ui/components/residence/DetailRow.kt @@ -0,0 +1,55 @@ +package com.mycrib.android.ui.components.residence + +import androidx.compose.foundation.layout.* +import androidx.compose.material3.* +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.vector.ImageVector +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.unit.dp +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.SquareFoot +import org.jetbrains.compose.ui.tooling.preview.Preview + +@Composable +fun DetailRow( + icon: ImageVector, + label: String, + value: String +) { + Row( + modifier = Modifier.fillMaxWidth(), + verticalAlignment = Alignment.CenterVertically + ) { + Icon( + icon, + contentDescription = null, + tint = MaterialTheme.colorScheme.primary, + modifier = Modifier.size(20.dp) + ) + Spacer(modifier = Modifier.width(8.dp)) + Text( + text = "$label: ", + style = MaterialTheme.typography.bodyMedium, + fontWeight = FontWeight.SemiBold + ) + Text( + text = value, + style = MaterialTheme.typography.bodyMedium, + color = MaterialTheme.colorScheme.onSurfaceVariant + ) + } +} + +@Preview +@Composable +fun DetailRowPreview() { + MaterialTheme { + DetailRow( + icon = Icons.Default.SquareFoot, + label = "Square Footage", + value = "1800 sq ft" + ) + } +} diff --git a/composeApp/src/commonMain/kotlin/com/example/mycrib/ui/components/residence/PropertyDetailItem.kt b/composeApp/src/commonMain/kotlin/com/example/mycrib/ui/components/residence/PropertyDetailItem.kt new file mode 100644 index 0000000..9eacc63 --- /dev/null +++ b/composeApp/src/commonMain/kotlin/com/example/mycrib/ui/components/residence/PropertyDetailItem.kt @@ -0,0 +1,54 @@ +package com.mycrib.android.ui.components.residence + +import androidx.compose.foundation.layout.* +import androidx.compose.material3.* +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.vector.ImageVector +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.unit.dp +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Bed +import org.jetbrains.compose.ui.tooling.preview.Preview + +@Composable +fun PropertyDetailItem( + icon: ImageVector, + value: String, + label: String +) { + Column( + horizontalAlignment = Alignment.CenterHorizontally, + verticalArrangement = Arrangement.spacedBy(4.dp) + ) { + Icon( + icon, + contentDescription = null, + tint = MaterialTheme.colorScheme.primary, + modifier = Modifier.size(32.dp) + ) + Text( + text = value, + style = MaterialTheme.typography.titleLarge, + fontWeight = FontWeight.Bold + ) + Text( + text = label, + style = MaterialTheme.typography.bodySmall, + color = MaterialTheme.colorScheme.onSurfaceVariant + ) + } +} + +@Preview +@Composable +fun PropertyDetailItemPreview() { + MaterialTheme { + PropertyDetailItem( + icon = Icons.Default.Bed, + value = "3", + label = "Bedrooms" + ) + } +} diff --git a/composeApp/src/commonMain/kotlin/com/example/mycrib/ui/components/residence/TaskStatChip.kt b/composeApp/src/commonMain/kotlin/com/example/mycrib/ui/components/residence/TaskStatChip.kt new file mode 100644 index 0000000..35c23a8 --- /dev/null +++ b/composeApp/src/commonMain/kotlin/com/example/mycrib/ui/components/residence/TaskStatChip.kt @@ -0,0 +1,58 @@ +package com.mycrib.android.ui.components.residence + +import androidx.compose.foundation.layout.* +import androidx.compose.material3.* +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.vector.ImageVector +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.unit.dp +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.CheckCircle +import org.jetbrains.compose.ui.tooling.preview.Preview + +@Composable +fun TaskStatChip( + icon: ImageVector, + value: String, + label: String, + color: Color +) { + Row( + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.spacedBy(4.dp) + ) { + Icon( + icon, + contentDescription = null, + modifier = Modifier.size(16.dp), + tint = color + ) + Text( + text = "$value", + style = MaterialTheme.typography.titleMedium, + fontWeight = FontWeight.Bold, + color = color + ) + Text( + text = label, + style = MaterialTheme.typography.bodySmall, + color = MaterialTheme.colorScheme.onSurfaceVariant + ) + } +} + +@Preview +@Composable +fun TaskStatChipPreview() { + MaterialTheme { + TaskStatChip( + icon = Icons.Default.CheckCircle, + value = "12", + label = "Completed", + color = MaterialTheme.colorScheme.tertiary + ) + } +} diff --git a/composeApp/src/commonMain/kotlin/com/example/mycrib/ui/components/task/SimpleTaskListItem.kt b/composeApp/src/commonMain/kotlin/com/example/mycrib/ui/components/task/SimpleTaskListItem.kt new file mode 100644 index 0000000..d162331 --- /dev/null +++ b/composeApp/src/commonMain/kotlin/com/example/mycrib/ui/components/task/SimpleTaskListItem.kt @@ -0,0 +1,120 @@ +package com.mycrib.android.ui.components.task + +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material3.* +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.unit.dp +import org.jetbrains.compose.ui.tooling.preview.Preview + +@Composable +fun SimpleTaskListItem( + title: String, + description: String?, + priority: String?, + status: String?, + dueDate: String?, + isOverdue: Boolean = false, + modifier: Modifier = Modifier +) { + Card( + modifier = modifier.fillMaxWidth(), + shape = RoundedCornerShape(12.dp), + elevation = CardDefaults.cardElevation(defaultElevation = 2.dp) + ) { + Column( + modifier = Modifier + .fillMaxWidth() + .padding(16.dp) + ) { + Row( + modifier = Modifier.fillMaxWidth(), + horizontalArrangement = Arrangement.SpaceBetween + ) { + Text( + text = title, + style = MaterialTheme.typography.titleMedium, + fontWeight = FontWeight.SemiBold + ) + // Priority badge + Surface( + color = when (priority?.lowercase()) { + "urgent" -> MaterialTheme.colorScheme.error + "high" -> MaterialTheme.colorScheme.errorContainer + "medium" -> MaterialTheme.colorScheme.primaryContainer + else -> MaterialTheme.colorScheme.surfaceVariant + }, + shape = MaterialTheme.shapes.small + ) { + Text( + text = priority?.uppercase() ?: "LOW", + modifier = Modifier.padding(horizontal = 8.dp, vertical = 4.dp), + style = MaterialTheme.typography.labelSmall + ) + } + } + + if (description != null) { + Spacer(modifier = Modifier.height(4.dp)) + Text( + text = description, + style = MaterialTheme.typography.bodyMedium, + color = MaterialTheme.colorScheme.onSurfaceVariant + ) + } + + Spacer(modifier = Modifier.height(8.dp)) + + Row( + modifier = Modifier.fillMaxWidth(), + horizontalArrangement = Arrangement.SpaceBetween + ) { + Text( + text = "Status: ${status?.replaceFirstChar { it.uppercase() } ?: "Unknown"}", + style = MaterialTheme.typography.bodySmall + ) + if (dueDate != null) { + Text( + text = "Due: $dueDate", + style = MaterialTheme.typography.bodySmall, + color = if (isOverdue) + MaterialTheme.colorScheme.error + else + MaterialTheme.colorScheme.onSurfaceVariant + ) + } + } + } + } +} + +@Preview +@Composable +fun SimpleTaskListItemPreview() { + MaterialTheme { + Column( + modifier = Modifier.padding(16.dp), + verticalArrangement = Arrangement.spacedBy(8.dp) + ) { + SimpleTaskListItem( + title = "Fix leaky faucet", + description = "Kitchen sink is dripping", + priority = "high", + status = "pending", + dueDate = "2024-12-20", + isOverdue = false + ) + + SimpleTaskListItem( + title = "Paint living room", + description = null, + priority = "medium", + status = "in_progress", + dueDate = "2024-12-15", + isOverdue = true + ) + } + } +} diff --git a/composeApp/src/commonMain/kotlin/com/example/mycrib/ui/components/task/TaskCard.kt b/composeApp/src/commonMain/kotlin/com/example/mycrib/ui/components/task/TaskCard.kt new file mode 100644 index 0000000..1dffb47 --- /dev/null +++ b/composeApp/src/commonMain/kotlin/com/example/mycrib/ui/components/task/TaskCard.kt @@ -0,0 +1,360 @@ +package com.mycrib.android.ui.components.task + +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.* +import androidx.compose.material3.* +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.unit.dp +import com.mycrib.shared.models.TaskDetail +import com.mycrib.shared.models.TaskCategory +import com.mycrib.shared.models.TaskPriority +import com.mycrib.shared.models.TaskFrequency +import com.mycrib.shared.models.TaskStatus +import org.jetbrains.compose.ui.tooling.preview.Preview + +@Composable +fun TaskCard( + task: TaskDetail, + onCompleteClick: (() -> Unit)?, + onEditClick: () -> Unit, + onCancelClick: (() -> Unit)?, + onUncancelClick: (() -> Unit)? +) { + Card( + modifier = Modifier.fillMaxWidth(), + shape = RoundedCornerShape(16.dp), + elevation = CardDefaults.cardElevation(defaultElevation = 2.dp) + ) { + Column( + modifier = Modifier + .fillMaxWidth() + .padding(20.dp) + ) { + Row( + modifier = Modifier.fillMaxWidth(), + horizontalArrangement = Arrangement.SpaceBetween, + verticalAlignment = Alignment.CenterVertically + ) { + Column(modifier = Modifier.weight(1f)) { + Text( + text = task.title, + style = MaterialTheme.typography.titleLarge, + fontWeight = FontWeight.Bold + ) + Spacer(modifier = Modifier.height(4.dp)) + Surface( + color = MaterialTheme.colorScheme.secondaryContainer, + shape = RoundedCornerShape(8.dp) + ) { + Text( + text = task.category.name, + modifier = Modifier.padding(horizontal = 8.dp, vertical = 4.dp), + style = MaterialTheme.typography.labelMedium, + color = MaterialTheme.colorScheme.onSecondaryContainer + ) + } + } + + Column( + horizontalAlignment = Alignment.End, + verticalArrangement = Arrangement.spacedBy(6.dp) + ) { + Surface( + color = when (task.priority.name.lowercase()) { + "urgent" -> MaterialTheme.colorScheme.error + "high" -> MaterialTheme.colorScheme.errorContainer + "medium" -> MaterialTheme.colorScheme.primaryContainer + else -> MaterialTheme.colorScheme.surfaceVariant + }, + shape = RoundedCornerShape(10.dp) + ) { + Text( + text = task.priority.name.uppercase(), + modifier = Modifier.padding(horizontal = 10.dp, vertical = 6.dp), + style = MaterialTheme.typography.labelMedium, + fontWeight = FontWeight.Bold + ) + } + + if (task.status != null) { + Surface( + color = MaterialTheme.colorScheme.tertiaryContainer, + shape = RoundedCornerShape(10.dp) + ) { + Text( + text = task.status.name.uppercase(), + modifier = Modifier.padding(horizontal = 10.dp, vertical = 6.dp), + style = MaterialTheme.typography.labelMedium, + color = MaterialTheme.colorScheme.onTertiaryContainer + ) + } + } + } + } + + if (task.description != null) { + Spacer(modifier = Modifier.height(12.dp)) + Text( + text = task.description, + style = MaterialTheme.typography.bodyMedium, + color = MaterialTheme.colorScheme.onSurfaceVariant + ) + } + + Spacer(modifier = Modifier.height(12.dp)) + HorizontalDivider() + Spacer(modifier = Modifier.height(12.dp)) + + Row( + modifier = Modifier.fillMaxWidth(), + horizontalArrangement = Arrangement.SpaceBetween + ) { + Row(verticalAlignment = Alignment.CenterVertically) { + Icon( + Icons.Default.CalendarToday, + contentDescription = null, + modifier = Modifier.size(16.dp), + tint = MaterialTheme.colorScheme.primary + ) + Spacer(modifier = Modifier.width(4.dp)) + Text( + text = task.nextScheduledDate ?: task.dueDate, + style = MaterialTheme.typography.bodySmall, + fontWeight = FontWeight.SemiBold + ) + } + task.estimatedCost?.let { + Row(verticalAlignment = Alignment.CenterVertically) { + Icon( + Icons.Default.AttachMoney, + contentDescription = null, + modifier = Modifier.size(16.dp), + tint = MaterialTheme.colorScheme.tertiary + ) + Spacer(modifier = Modifier.width(4.dp)) + Text( + text = "$$it", + style = MaterialTheme.typography.bodySmall, + fontWeight = FontWeight.SemiBold, + color = MaterialTheme.colorScheme.tertiary + ) + } + } + } + + // Show completions + if (task.completions.isNotEmpty()) { + Spacer(modifier = Modifier.height(16.dp)) + HorizontalDivider() + Spacer(modifier = Modifier.height(12.dp)) + + Row(verticalAlignment = Alignment.CenterVertically) { + Icon( + Icons.Default.CheckCircle, + contentDescription = null, + tint = MaterialTheme.colorScheme.tertiary, + modifier = Modifier.size(20.dp) + ) + Spacer(modifier = Modifier.width(8.dp)) + Text( + text = "Completions (${task.completions.size})", + style = MaterialTheme.typography.titleSmall, + fontWeight = FontWeight.Bold, + color = MaterialTheme.colorScheme.tertiary + ) + } + + task.completions.forEach { completion -> + Spacer(modifier = Modifier.height(12.dp)) + Card( + colors = CardDefaults.cardColors( + containerColor = MaterialTheme.colorScheme.surfaceVariant + ), + shape = RoundedCornerShape(12.dp) + ) { + Column( + modifier = Modifier + .fillMaxWidth() + .padding(16.dp) + ) { + Row( + modifier = Modifier.fillMaxWidth(), + horizontalArrangement = Arrangement.SpaceBetween, + verticalAlignment = Alignment.CenterVertically + ) { + Text( + text = completion.completionDate.split("T")[0], + style = MaterialTheme.typography.bodyMedium, + fontWeight = FontWeight.Bold, + color = MaterialTheme.colorScheme.primary + ) + completion.rating?.let { rating -> + Surface( + color = MaterialTheme.colorScheme.tertiaryContainer, + shape = RoundedCornerShape(8.dp) + ) { + Text( + text = "$rating★", + modifier = Modifier.padding(horizontal = 8.dp, vertical = 4.dp), + style = MaterialTheme.typography.labelMedium, + fontWeight = FontWeight.Bold, + color = MaterialTheme.colorScheme.onTertiaryContainer + ) + } + } + } + + completion.completedByName?.let { + Spacer(modifier = Modifier.height(8.dp)) + Text( + text = "By: $it", + style = MaterialTheme.typography.bodySmall, + fontWeight = FontWeight.Medium + ) + } + + completion.actualCost?.let { + Text( + text = "Cost: $$it", + style = MaterialTheme.typography.bodySmall, + color = MaterialTheme.colorScheme.tertiary, + fontWeight = FontWeight.Medium + ) + } + + completion.notes?.let { + Spacer(modifier = Modifier.height(8.dp)) + Text( + text = it, + style = MaterialTheme.typography.bodySmall, + color = MaterialTheme.colorScheme.onSurfaceVariant + ) + } + } + } + } + } + + // Show complete task button based on API logic + if (task.showCompletedButton && onCompleteClick != null) { + Spacer(modifier = Modifier.height(16.dp)) + Button( + onClick = onCompleteClick, + modifier = Modifier.fillMaxWidth(), + shape = RoundedCornerShape(12.dp) + ) { + Icon( + Icons.Default.CheckCircle, + contentDescription = null, + modifier = Modifier.size(20.dp) + ) + Spacer(modifier = Modifier.width(8.dp)) + Text( + "Complete Task", + style = MaterialTheme.typography.titleSmall, + fontWeight = FontWeight.SemiBold + ) + } + } + + // Action buttons row + Spacer(modifier = Modifier.height(12.dp)) + Row( + modifier = Modifier.fillMaxWidth(), + horizontalArrangement = Arrangement.spacedBy(8.dp) + ) { + // Edit button + OutlinedButton( + onClick = onEditClick, + modifier = Modifier.weight(1f), + shape = RoundedCornerShape(12.dp) + ) { + Icon( + Icons.Default.Edit, + contentDescription = null, + modifier = Modifier.size(18.dp) + ) + Spacer(modifier = Modifier.width(4.dp)) + Text("Edit") + } + + // Cancel or Uncancel button + when { + onCancelClick != null -> { + OutlinedButton( + onClick = onCancelClick, + modifier = Modifier.weight(1f), + shape = RoundedCornerShape(12.dp), + colors = ButtonDefaults.outlinedButtonColors( + contentColor = MaterialTheme.colorScheme.error + ) + ) { + Icon( + Icons.Default.Cancel, + contentDescription = null, + modifier = Modifier.size(18.dp) + ) + Spacer(modifier = Modifier.width(4.dp)) + Text("Cancel") + } + } + onUncancelClick != null -> { + Button( + onClick = onUncancelClick, + modifier = Modifier.weight(1f), + shape = RoundedCornerShape(12.dp), + colors = ButtonDefaults.buttonColors( + containerColor = MaterialTheme.colorScheme.secondary + ) + ) { + Icon( + Icons.Default.Undo, + contentDescription = null, + modifier = Modifier.size(18.dp) + ) + Spacer(modifier = Modifier.width(4.dp)) + Text("Restore") + } + } + } + } + } + } +} + +@Preview +@Composable +fun TaskCardPreview() { + MaterialTheme { + TaskCard( + task = TaskDetail( + id = 1, + residence = 1, + title = "Clean Gutters", + description = "Remove all debris from gutters and downspouts", + category = TaskCategory(id = 1, name = "maintenance", description = ""), + priority = TaskPriority(id = 2, name = "medium", displayName = "Medium", description = ""), + frequency = TaskFrequency(id = 1, name = "monthly", displayName = "Monthly"), + status = TaskStatus(id = 1, name = "pending", displayName = "Pending", description = ""), + dueDate = "2024-12-15", + estimatedCost = "150.00", + actualCost = null, + notes = null, + createdAt = "2024-01-01T00:00:00Z", + updatedAt = "2024-01-01T00:00:00Z", + nextScheduledDate = "2024-12-15", + showCompletedButton = true, + completions = emptyList() + ), + onCompleteClick = {}, + onEditClick = {}, + onCancelClick = {}, + onUncancelClick = null + ) + } +} diff --git a/composeApp/src/commonMain/kotlin/com/example/mycrib/ui/screens/LoginScreen.kt b/composeApp/src/commonMain/kotlin/com/example/mycrib/ui/screens/LoginScreen.kt index acbfde0..61f21fb 100644 --- a/composeApp/src/commonMain/kotlin/com/example/mycrib/ui/screens/LoginScreen.kt +++ b/composeApp/src/commonMain/kotlin/com/example/mycrib/ui/screens/LoginScreen.kt @@ -14,6 +14,8 @@ import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.input.PasswordVisualTransformation import androidx.compose.ui.unit.dp import androidx.lifecycle.viewmodel.compose.viewModel +import com.mycrib.android.ui.components.auth.AuthHeader +import com.mycrib.android.ui.components.common.ErrorCard import com.mycrib.android.viewmodel.AuthViewModel import com.mycrib.shared.network.ApiResult @@ -67,25 +69,10 @@ fun LoginScreen( horizontalAlignment = Alignment.CenterHorizontally, verticalArrangement = Arrangement.spacedBy(20.dp) ) { - // App Logo/Icon - Icon( - Icons.Default.Home, - contentDescription = null, - modifier = Modifier.size(64.dp), - tint = MaterialTheme.colorScheme.primary - ) - - Text( - text = "myCrib", - style = MaterialTheme.typography.displaySmall, - fontWeight = FontWeight.Bold, - color = MaterialTheme.colorScheme.primary - ) - - Text( - text = "Manage your properties with ease", - style = MaterialTheme.typography.bodyMedium, - color = MaterialTheme.colorScheme.onSurfaceVariant + AuthHeader( + icon = Icons.Default.Home, + title = "myCrib", + subtitle = "Manage your properties with ease" ) Spacer(modifier = Modifier.height(8.dp)) @@ -115,21 +102,7 @@ fun LoginScreen( shape = RoundedCornerShape(12.dp) ) - if (errorMessage.isNotEmpty()) { - Card( - colors = CardDefaults.cardColors( - containerColor = MaterialTheme.colorScheme.errorContainer - ), - modifier = Modifier.fillMaxWidth() - ) { - Text( - text = errorMessage, - color = MaterialTheme.colorScheme.onErrorContainer, - modifier = Modifier.padding(12.dp), - style = MaterialTheme.typography.bodySmall - ) - } - } + ErrorCard(message = errorMessage) Button( onClick = { diff --git a/composeApp/src/commonMain/kotlin/com/example/mycrib/ui/screens/RegisterScreen.kt b/composeApp/src/commonMain/kotlin/com/example/mycrib/ui/screens/RegisterScreen.kt index 96781d5..dc27444 100644 --- a/composeApp/src/commonMain/kotlin/com/example/mycrib/ui/screens/RegisterScreen.kt +++ b/composeApp/src/commonMain/kotlin/com/example/mycrib/ui/screens/RegisterScreen.kt @@ -14,6 +14,8 @@ import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.input.PasswordVisualTransformation import androidx.compose.ui.unit.dp import androidx.lifecycle.viewmodel.compose.viewModel +import com.mycrib.android.ui.components.auth.AuthHeader +import com.mycrib.android.ui.components.common.ErrorCard import com.mycrib.android.viewmodel.AuthViewModel import com.mycrib.shared.network.ApiResult @@ -68,24 +70,10 @@ fun RegisterScreen( ) { Spacer(modifier = Modifier.height(8.dp)) - Icon( - Icons.Default.PersonAdd, - contentDescription = null, - modifier = Modifier.size(64.dp), - tint = MaterialTheme.colorScheme.primary - ) - - Text( - text = "Join myCrib", - style = MaterialTheme.typography.headlineMedium, - fontWeight = FontWeight.Bold, - color = MaterialTheme.colorScheme.onSurface - ) - - Text( - text = "Start managing your properties today", - style = MaterialTheme.typography.bodyLarge, - color = MaterialTheme.colorScheme.onSurfaceVariant + AuthHeader( + icon = Icons.Default.PersonAdd, + title = "Join myCrib", + subtitle = "Start managing your properties today" ) Spacer(modifier = Modifier.height(16.dp)) @@ -140,22 +128,7 @@ fun RegisterScreen( shape = RoundedCornerShape(12.dp) ) - if (errorMessage.isNotEmpty()) { - Card( - colors = CardDefaults.cardColors( - containerColor = MaterialTheme.colorScheme.errorContainer - ), - modifier = Modifier.fillMaxWidth(), - shape = RoundedCornerShape(12.dp) - ) { - Text( - text = errorMessage, - color = MaterialTheme.colorScheme.onErrorContainer, - modifier = Modifier.padding(16.dp), - style = MaterialTheme.typography.bodyMedium - ) - } - } + ErrorCard(message = errorMessage) Spacer(modifier = Modifier.height(8.dp)) 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 a4dd3b8..0b8739c 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 @@ -15,6 +15,10 @@ import androidx.compose.ui.unit.dp import androidx.lifecycle.viewmodel.compose.viewModel import com.mycrib.android.ui.components.AddNewTaskDialog import com.mycrib.android.ui.components.CompleteTaskDialog +import com.mycrib.android.ui.components.common.InfoCard +import com.mycrib.android.ui.components.residence.PropertyDetailItem +import com.mycrib.android.ui.components.residence.DetailRow +import com.mycrib.android.ui.components.task.TaskCard import com.mycrib.android.viewmodel.ResidenceViewModel import com.mycrib.android.viewmodel.TaskCompletionViewModel import com.mycrib.android.viewmodel.TaskViewModel @@ -494,412 +498,3 @@ fun ResidenceDetailScreen( } } } - -@Composable -private fun InfoCard( - icon: androidx.compose.ui.graphics.vector.ImageVector, - title: String, - content: @Composable ColumnScope.() -> Unit -) { - Card( - modifier = Modifier.fillMaxWidth(), - shape = RoundedCornerShape(16.dp), - elevation = CardDefaults.cardElevation(defaultElevation = 2.dp) - ) { - Column( - modifier = Modifier - .fillMaxWidth() - .padding(20.dp), - verticalArrangement = Arrangement.spacedBy(8.dp) - ) { - Row( - verticalAlignment = Alignment.CenterVertically - ) { - Icon( - icon, - contentDescription = null, - tint = MaterialTheme.colorScheme.primary, - modifier = Modifier.size(24.dp) - ) - Spacer(modifier = Modifier.width(8.dp)) - Text( - text = title, - style = MaterialTheme.typography.titleMedium, - fontWeight = FontWeight.Bold, - color = MaterialTheme.colorScheme.primary - ) - } - Spacer(modifier = Modifier.height(4.dp)) - content() - } - } -} - -@Composable -private fun PropertyDetailItem( - icon: androidx.compose.ui.graphics.vector.ImageVector, - value: String, - label: String -) { - Column( - horizontalAlignment = Alignment.CenterHorizontally, - verticalArrangement = Arrangement.spacedBy(4.dp) - ) { - Icon( - icon, - contentDescription = null, - tint = MaterialTheme.colorScheme.primary, - modifier = Modifier.size(32.dp) - ) - Text( - text = value, - style = MaterialTheme.typography.titleLarge, - fontWeight = FontWeight.Bold - ) - Text( - text = label, - style = MaterialTheme.typography.bodySmall, - color = MaterialTheme.colorScheme.onSurfaceVariant - ) - } -} - -@Composable -private fun DetailRow( - icon: androidx.compose.ui.graphics.vector.ImageVector, - label: String, - value: String -) { - Row( - modifier = Modifier.fillMaxWidth(), - verticalAlignment = Alignment.CenterVertically - ) { - Icon( - icon, - contentDescription = null, - tint = MaterialTheme.colorScheme.primary, - modifier = Modifier.size(20.dp) - ) - Spacer(modifier = Modifier.width(8.dp)) - Text( - text = "$label: ", - style = MaterialTheme.typography.bodyMedium, - fontWeight = FontWeight.SemiBold - ) - Text( - text = value, - style = MaterialTheme.typography.bodyMedium, - color = MaterialTheme.colorScheme.onSurfaceVariant - ) - } -} - -@Composable -fun TaskCard( - task: TaskDetail, - onCompleteClick: (() -> Unit)?, - onEditClick: () -> Unit, - onCancelClick: (() -> Unit)?, - onUncancelClick: (() -> Unit)? -) { - Card( - modifier = Modifier.fillMaxWidth(), - shape = RoundedCornerShape(16.dp), - elevation = CardDefaults.cardElevation(defaultElevation = 2.dp) - ) { - Column( - modifier = Modifier - .fillMaxWidth() - .padding(20.dp) - ) { - Row( - modifier = Modifier.fillMaxWidth(), - horizontalArrangement = Arrangement.SpaceBetween, - verticalAlignment = Alignment.CenterVertically - ) { - Column(modifier = Modifier.weight(1f)) { - Text( - text = task.title, - style = MaterialTheme.typography.titleLarge, - fontWeight = FontWeight.Bold - ) - Spacer(modifier = Modifier.height(4.dp)) - Surface( - color = MaterialTheme.colorScheme.secondaryContainer, - shape = RoundedCornerShape(8.dp) - ) { - Text( - text = task.category.name, - modifier = Modifier.padding(horizontal = 8.dp, vertical = 4.dp), - style = MaterialTheme.typography.labelMedium, - color = MaterialTheme.colorScheme.onSecondaryContainer - ) - } - } - - Column( - horizontalAlignment = Alignment.End, - verticalArrangement = Arrangement.spacedBy(6.dp) - ) { - Surface( - color = when (task.priority.name.lowercase()) { - "urgent" -> MaterialTheme.colorScheme.error - "high" -> MaterialTheme.colorScheme.errorContainer - "medium" -> MaterialTheme.colorScheme.primaryContainer - else -> MaterialTheme.colorScheme.surfaceVariant - }, - shape = RoundedCornerShape(10.dp) - ) { - Text( - text = task.priority.name.uppercase(), - modifier = Modifier.padding(horizontal = 10.dp, vertical = 6.dp), - style = MaterialTheme.typography.labelMedium, - fontWeight = FontWeight.Bold - ) - } - - if (task.status != null) { - Surface( - color = MaterialTheme.colorScheme.tertiaryContainer, - shape = RoundedCornerShape(10.dp) - ) { - Text( - text = task.status.name.uppercase(), - modifier = Modifier.padding(horizontal = 10.dp, vertical = 6.dp), - style = MaterialTheme.typography.labelMedium, - color = MaterialTheme.colorScheme.onTertiaryContainer - ) - } - } - } - } - - if (task.description != null) { - Spacer(modifier = Modifier.height(12.dp)) - Text( - text = task.description, - style = MaterialTheme.typography.bodyMedium, - color = MaterialTheme.colorScheme.onSurfaceVariant - ) - } - - Spacer(modifier = Modifier.height(12.dp)) - HorizontalDivider() - Spacer(modifier = Modifier.height(12.dp)) - - Row( - modifier = Modifier.fillMaxWidth(), - horizontalArrangement = Arrangement.SpaceBetween - ) { - Row(verticalAlignment = Alignment.CenterVertically) { - Icon( - Icons.Default.CalendarToday, - contentDescription = null, - modifier = Modifier.size(16.dp), - tint = MaterialTheme.colorScheme.primary - ) - Spacer(modifier = Modifier.width(4.dp)) - Text( - text = task.nextScheduledDate ?: task.dueDate, - style = MaterialTheme.typography.bodySmall, - fontWeight = FontWeight.SemiBold - ) - } - task.estimatedCost?.let { - Row(verticalAlignment = Alignment.CenterVertically) { - Icon( - Icons.Default.AttachMoney, - contentDescription = null, - modifier = Modifier.size(16.dp), - tint = MaterialTheme.colorScheme.tertiary - ) - Spacer(modifier = Modifier.width(4.dp)) - Text( - text = "$$it", - style = MaterialTheme.typography.bodySmall, - fontWeight = FontWeight.SemiBold, - color = MaterialTheme.colorScheme.tertiary - ) - } - } - } - - // Show completions - if (task.completions.isNotEmpty()) { - Spacer(modifier = Modifier.height(16.dp)) - HorizontalDivider() - Spacer(modifier = Modifier.height(12.dp)) - - Row(verticalAlignment = Alignment.CenterVertically) { - Icon( - Icons.Default.CheckCircle, - contentDescription = null, - tint = MaterialTheme.colorScheme.tertiary, - modifier = Modifier.size(20.dp) - ) - Spacer(modifier = Modifier.width(8.dp)) - Text( - text = "Completions (${task.completions.size})", - style = MaterialTheme.typography.titleSmall, - fontWeight = FontWeight.Bold, - color = MaterialTheme.colorScheme.tertiary - ) - } - - task.completions.forEach { completion -> - Spacer(modifier = Modifier.height(12.dp)) - Card( - colors = CardDefaults.cardColors( - containerColor = MaterialTheme.colorScheme.surfaceVariant - ), - shape = RoundedCornerShape(12.dp) - ) { - Column( - modifier = Modifier - .fillMaxWidth() - .padding(16.dp) - ) { - Row( - modifier = Modifier.fillMaxWidth(), - horizontalArrangement = Arrangement.SpaceBetween, - verticalAlignment = Alignment.CenterVertically - ) { - Text( - text = completion.completionDate.split("T")[0], - style = MaterialTheme.typography.bodyMedium, - fontWeight = FontWeight.Bold, - color = MaterialTheme.colorScheme.primary - ) - completion.rating?.let { rating -> - Surface( - color = MaterialTheme.colorScheme.tertiaryContainer, - shape = RoundedCornerShape(8.dp) - ) { - Text( - text = "$rating★", - modifier = Modifier.padding(horizontal = 8.dp, vertical = 4.dp), - style = MaterialTheme.typography.labelMedium, - fontWeight = FontWeight.Bold, - color = MaterialTheme.colorScheme.onTertiaryContainer - ) - } - } - } - - completion.completedByName?.let { - Spacer(modifier = Modifier.height(8.dp)) - Text( - text = "By: $it", - style = MaterialTheme.typography.bodySmall, - fontWeight = FontWeight.Medium - ) - } - - completion.actualCost?.let { - Text( - text = "Cost: $$it", - style = MaterialTheme.typography.bodySmall, - color = MaterialTheme.colorScheme.tertiary, - fontWeight = FontWeight.Medium - ) - } - - completion.notes?.let { - Spacer(modifier = Modifier.height(8.dp)) - Text( - text = it, - style = MaterialTheme.typography.bodySmall, - color = MaterialTheme.colorScheme.onSurfaceVariant - ) - } - } - } - } - } - - // Show complete task button based on API logic - if (task.showCompletedButton && onCompleteClick != null) { - Spacer(modifier = Modifier.height(16.dp)) - Button( - onClick = onCompleteClick, - modifier = Modifier.fillMaxWidth(), - shape = RoundedCornerShape(12.dp) - ) { - Icon( - Icons.Default.CheckCircle, - contentDescription = null, - modifier = Modifier.size(20.dp) - ) - Spacer(modifier = Modifier.width(8.dp)) - Text( - "Complete Task", - style = MaterialTheme.typography.titleSmall, - fontWeight = FontWeight.SemiBold - ) - } - } - - // Action buttons row - Spacer(modifier = Modifier.height(12.dp)) - Row( - modifier = Modifier.fillMaxWidth(), - horizontalArrangement = Arrangement.spacedBy(8.dp) - ) { - // Edit button - OutlinedButton( - onClick = onEditClick, - modifier = Modifier.weight(1f), - shape = RoundedCornerShape(12.dp) - ) { - Icon( - Icons.Default.Edit, - contentDescription = null, - modifier = Modifier.size(18.dp) - ) - Spacer(modifier = Modifier.width(4.dp)) - Text("Edit") - } - - // Cancel or Uncancel button - when { - onCancelClick != null -> { - OutlinedButton( - onClick = onCancelClick, - modifier = Modifier.weight(1f), - shape = RoundedCornerShape(12.dp), - colors = ButtonDefaults.outlinedButtonColors( - contentColor = MaterialTheme.colorScheme.error - ) - ) { - Icon( - Icons.Default.Cancel, - contentDescription = null, - modifier = Modifier.size(18.dp) - ) - Spacer(modifier = Modifier.width(4.dp)) - Text("Cancel") - } - } - onUncancelClick != null -> { - Button( - onClick = onUncancelClick, - modifier = Modifier.weight(1f), - shape = RoundedCornerShape(12.dp), - colors = ButtonDefaults.buttonColors( - containerColor = MaterialTheme.colorScheme.secondary - ) - ) { - Icon( - Icons.Default.Undo, - contentDescription = null, - modifier = Modifier.size(18.dp) - ) - Spacer(modifier = Modifier.width(4.dp)) - Text("Restore") - } - } - } - } - } - } -} diff --git a/composeApp/src/commonMain/kotlin/com/example/mycrib/ui/screens/ResidencesScreen.kt b/composeApp/src/commonMain/kotlin/com/example/mycrib/ui/screens/ResidencesScreen.kt index af63ed4..9cc4d3c 100644 --- a/composeApp/src/commonMain/kotlin/com/example/mycrib/ui/screens/ResidencesScreen.kt +++ b/composeApp/src/commonMain/kotlin/com/example/mycrib/ui/screens/ResidencesScreen.kt @@ -14,6 +14,8 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.dp import androidx.lifecycle.viewmodel.compose.viewModel +import com.mycrib.android.ui.components.common.StatItem +import com.mycrib.android.ui.components.residence.TaskStatChip import com.mycrib.android.viewmodel.ResidenceViewModel import com.mycrib.shared.network.ApiResult @@ -299,64 +301,3 @@ fun ResidencesScreen( } } } - -@Composable -private fun StatItem( - icon: androidx.compose.ui.graphics.vector.ImageVector, - value: String, - label: String -) { - Column( - horizontalAlignment = Alignment.CenterHorizontally, - verticalArrangement = Arrangement.spacedBy(8.dp) - ) { - Icon( - icon, - contentDescription = null, - modifier = Modifier.size(28.dp), - tint = MaterialTheme.colorScheme.onPrimaryContainer - ) - Text( - text = value, - style = MaterialTheme.typography.headlineMedium, - fontWeight = FontWeight.Bold, - color = MaterialTheme.colorScheme.onPrimaryContainer - ) - Text( - text = label, - style = MaterialTheme.typography.bodySmall, - color = MaterialTheme.colorScheme.onPrimaryContainer.copy(alpha = 0.8f) - ) - } -} - -@Composable -private fun TaskStatChip( - icon: androidx.compose.ui.graphics.vector.ImageVector, - value: String, - label: String, - color: androidx.compose.ui.graphics.Color -) { - Row( - verticalAlignment = Alignment.CenterVertically, - horizontalArrangement = Arrangement.spacedBy(4.dp) - ) { - Icon( - icon, - contentDescription = null, - modifier = Modifier.size(16.dp), - tint = color - ) - Text( - text = "$value", - style = MaterialTheme.typography.titleMedium, - fontWeight = FontWeight.Bold, - color = color - ) - Text( - text = label, - style = MaterialTheme.typography.bodySmall, - color = MaterialTheme.colorScheme.onSurfaceVariant - ) - } -} diff --git a/composeApp/src/commonMain/kotlin/com/example/mycrib/ui/screens/TasksScreen.kt b/composeApp/src/commonMain/kotlin/com/example/mycrib/ui/screens/TasksScreen.kt index b26aefa..b88741b 100644 --- a/composeApp/src/commonMain/kotlin/com/example/mycrib/ui/screens/TasksScreen.kt +++ b/composeApp/src/commonMain/kotlin/com/example/mycrib/ui/screens/TasksScreen.kt @@ -10,6 +10,7 @@ import androidx.compose.runtime.* import androidx.compose.ui.Modifier import androidx.compose.ui.unit.dp import androidx.lifecycle.viewmodel.compose.viewModel +import com.mycrib.android.ui.components.task.SimpleTaskListItem import com.mycrib.android.viewmodel.TaskViewModel import com.mycrib.shared.network.ApiResult @@ -92,69 +93,14 @@ fun TasksScreen( verticalArrangement = Arrangement.spacedBy(8.dp) ) { items(tasks) { task -> - Card( - modifier = Modifier.fillMaxWidth() - ) { - Column( - modifier = Modifier - .fillMaxWidth() - .padding(16.dp) - ) { - Row( - modifier = Modifier.fillMaxWidth(), - horizontalArrangement = Arrangement.SpaceBetween - ) { - Text( - text = task.title, - style = MaterialTheme.typography.titleMedium - ) - // Priority badge - Surface( - color = when (task.priority) { - "urgent" -> MaterialTheme.colorScheme.error - "high" -> MaterialTheme.colorScheme.errorContainer - "medium" -> MaterialTheme.colorScheme.primaryContainer - else -> MaterialTheme.colorScheme.surfaceVariant - }, - shape = MaterialTheme.shapes.small - ) { - Text( - text = task.priority?.uppercase() ?: "LOW", - modifier = Modifier.padding(horizontal = 8.dp, vertical = 4.dp), - style = MaterialTheme.typography.labelSmall - ) - } - } - Spacer(modifier = Modifier.height(4.dp)) - if (task.description != null) { - Text( - text = task.description, - style = MaterialTheme.typography.bodyMedium, - color = MaterialTheme.colorScheme.onSurfaceVariant - ) - Spacer(modifier = Modifier.height(8.dp)) - } - Row( - modifier = Modifier.fillMaxWidth(), - horizontalArrangement = Arrangement.SpaceBetween - ) { - Text( - text = "Status: ${task.status?.replaceFirstChar { it.uppercase() }}", - style = MaterialTheme.typography.bodySmall - ) - if (task.dueDate != null) { - Text( - text = "Due: ${task.dueDate}", - style = MaterialTheme.typography.bodySmall, - color = if (task.isOverdue == true) - MaterialTheme.colorScheme.error - else - MaterialTheme.colorScheme.onSurfaceVariant - ) - } - } - } - } + SimpleTaskListItem( + title = task.title, + description = task.description, + priority = task.priority, + status = task.status, + dueDate = task.dueDate, + isOverdue = task.isOverdue == true + ) } } }