package com.syncflow.ui.log import androidx.compose.foundation.layout.* import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.items 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.* import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.unit.dp import androidx.hilt.navigation.compose.hiltViewModel import com.syncflow.domain.model.SyncEventType import com.syncflow.ui.shared.iconAndTint import com.syncflow.ui.shared.label import java.time.Duration import java.time.Instant import java.time.ZoneId import java.time.format.DateTimeFormatter import java.time.format.FormatStyle @Composable fun LogScreen( modifier: Modifier = Modifier, vm: LogViewModel = hiltViewModel(), ) { val entries by vm.entries.collectAsState() if (entries.isEmpty()) { Box( modifier = modifier.fillMaxSize(), contentAlignment = Alignment.Center, ) { Column( horizontalAlignment = Alignment.CenterHorizontally, verticalArrangement = Arrangement.spacedBy(8.dp), ) { Icon( Icons.Default.Notifications, contentDescription = null, modifier = Modifier.size(72.dp), tint = MaterialTheme.colorScheme.primary.copy(alpha = 0.35f), ) Text("No activity yet", style = MaterialTheme.typography.titleMedium) Text( "Sync events will appear here", style = MaterialTheme.typography.bodySmall, color = MaterialTheme.colorScheme.onSurfaceVariant, ) } } } else { LazyColumn( modifier = modifier.fillMaxSize(), contentPadding = PaddingValues(horizontal = 16.dp, vertical = 8.dp), verticalArrangement = Arrangement.spacedBy(0.dp), ) { // Group entries by calendar date val grouped = entries.groupBy { entry -> entry.event.timestamp.atZone(ZoneId.systemDefault()).toLocalDate() } grouped.forEach { (date, dayEntries) -> item(key = date.toString()) { Text( text = date.toRelativeLabel(), style = MaterialTheme.typography.labelMedium, color = MaterialTheme.colorScheme.primary, modifier = Modifier.padding(top = 16.dp, bottom = 4.dp), ) } items(dayEntries, key = { it.event.id }) { entry -> LogEntryRow(entry) HorizontalDivider( color = MaterialTheme.colorScheme.outline.copy(alpha = 0.3f), modifier = Modifier.padding(start = 52.dp), ) } } item { Spacer(Modifier.height(80.dp)) } } } } @Composable private fun LogEntryRow(entry: LogEntry) { val (icon, tint) = entry.event.eventType.iconAndTint() val timeFmt = DateTimeFormatter.ofLocalizedTime(FormatStyle.SHORT) val timeStr = entry.event.timestamp.atZone(ZoneId.systemDefault()).let { timeFmt.format(it) } Row( modifier = Modifier .fillMaxWidth() .padding(vertical = 10.dp), verticalAlignment = Alignment.Top, ) { // Icon bubble Surface( shape = RoundedCornerShape(10.dp), color = tint.copy(alpha = 0.12f), modifier = Modifier.size(36.dp), ) { Box(contentAlignment = Alignment.Center) { Icon(icon, contentDescription = null, Modifier.size(18.dp), tint = tint) } } Spacer(Modifier.width(12.dp)) Column(modifier = Modifier.weight(1f)) { Row( modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween, verticalAlignment = Alignment.CenterVertically, ) { Text( entry.pairName, style = MaterialTheme.typography.labelMedium, color = MaterialTheme.colorScheme.primary, modifier = Modifier.weight(1f, fill = false), ) Text( timeStr, style = MaterialTheme.typography.labelSmall, color = MaterialTheme.colorScheme.onSurfaceVariant, ) } Spacer(Modifier.height(2.dp)) Text( text = entry.event.eventType.label(), style = MaterialTheme.typography.bodySmall, ) val detail = entry.event.filePath ?: entry.event.message if (detail != null) { Text( text = detail, style = MaterialTheme.typography.labelSmall, color = MaterialTheme.colorScheme.onSurfaceVariant, maxLines = 1, ) } } } } private fun java.time.LocalDate.toRelativeLabel(): String { val today = java.time.LocalDate.now() return when { this == today -> "Today" this == today.minusDays(1) -> "Yesterday" else -> DateTimeFormatter.ofLocalizedDate(FormatStyle.MEDIUM).format(this) } }