diff --git a/app/src/main/java/de/krisenvorrat/app/ui/item/ItemFormScreen.kt b/app/src/main/java/de/krisenvorrat/app/ui/item/ItemFormScreen.kt index b94fdfb..4716fc5 100644 --- a/app/src/main/java/de/krisenvorrat/app/ui/item/ItemFormScreen.kt +++ b/app/src/main/java/de/krisenvorrat/app/ui/item/ItemFormScreen.kt @@ -1,6 +1,9 @@ package de.krisenvorrat.app.ui.item +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth @@ -13,8 +16,7 @@ import androidx.compose.material.icons.Icons import androidx.compose.material.icons.automirrored.filled.ArrowBack import androidx.compose.material.icons.filled.Check import androidx.compose.material.icons.filled.DateRange -import androidx.compose.material3.DatePicker -import androidx.compose.material3.DatePickerDialog +import androidx.compose.material3.AlertDialog import androidx.compose.material3.DropdownMenuItem import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.ExposedDropdownMenuBox @@ -27,7 +29,6 @@ import androidx.compose.material3.Scaffold import androidx.compose.material3.Text import androidx.compose.material3.TextButton import androidx.compose.material3.TopAppBar -import androidx.compose.material3.rememberDatePickerState import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue @@ -39,9 +40,8 @@ import androidx.compose.ui.text.input.KeyboardType import androidx.compose.ui.unit.dp import androidx.hilt.navigation.compose.hiltViewModel import androidx.lifecycle.compose.collectAsStateWithLifecycle -import java.time.Instant import java.time.LocalDate -import java.time.ZoneId +import java.time.YearMonth import java.time.format.DateTimeFormatter @OptIn(ExperimentalMaterial3Api::class) @@ -311,8 +311,8 @@ private fun ExpiryDateField( expiryDate: LocalDate?, onDateSelected: (LocalDate?) -> Unit ) { - var isDatePickerVisible by remember { mutableStateOf(false) } - val formatter = DateTimeFormatter.ofPattern("dd.MM.yyyy") + var isPickerVisible by remember { mutableStateOf(false) } + val formatter = remember { DateTimeFormatter.ofPattern("MM/yyyy") } val displayValue = expiryDate?.format(formatter) ?: "" OutlinedTextField( @@ -321,51 +321,140 @@ private fun ExpiryDateField( readOnly = true, label = { Text("Ablaufdatum (MHD)") }, trailingIcon = { - IconButton(onClick = { isDatePickerVisible = true }) { + IconButton(onClick = { isPickerVisible = true }) { Icon( imageVector = Icons.Default.DateRange, contentDescription = "Datum wählen" ) } }, - modifier = Modifier.fillMaxWidth() + modifier = Modifier + .fillMaxWidth() + .clickable { isPickerVisible = true } ) - if (isDatePickerVisible) { - val initialMillis = expiryDate - ?.atStartOfDay(ZoneId.of("UTC")) - ?.toInstant() - ?.toEpochMilli() - val datePickerState = rememberDatePickerState(initialSelectedDateMillis = initialMillis) - - DatePickerDialog( - onDismissRequest = { isDatePickerVisible = false }, - confirmButton = { - TextButton( - onClick = { - val selectedMillis = datePickerState.selectedDateMillis - if (selectedMillis != null) { - val selectedDate = Instant.ofEpochMilli(selectedMillis) - .atZone(ZoneId.of("UTC")) - .toLocalDate() - onDateSelected(selectedDate) - } - isDatePickerVisible = false - } - ) { - Text("OK") - } + if (isPickerVisible) { + MonthYearPickerDialog( + initialDate = expiryDate, + onConfirm = { yearMonth -> + onDateSelected(yearMonth.atEndOfMonth()) + isPickerVisible = false }, - dismissButton = { - TextButton(onClick = { - onDateSelected(null) - isDatePickerVisible = false - }) { - Text("Entfernen") - } - } - ) { - DatePicker(state = datePickerState) - } + onRemove = { + onDateSelected(null) + isPickerVisible = false + }, + onDismiss = { isPickerVisible = false } + ) } } + +@OptIn(ExperimentalMaterial3Api::class) +@Composable +private fun MonthYearPickerDialog( + initialDate: LocalDate?, + onConfirm: (YearMonth) -> Unit, + onRemove: () -> Unit, + onDismiss: () -> Unit +) { + val today = remember { LocalDate.now() } + val initialYearMonth = if (initialDate != null) YearMonth.from(initialDate) else YearMonth.from(today) + + var selectedMonth by remember { mutableStateOf(initialYearMonth.monthValue) } + var selectedYear by remember { mutableStateOf(initialYearMonth.year) } + + val monthNames = remember { + listOf( + "Januar", "Februar", "März", "April", "Mai", "Juni", + "Juli", "August", "September", "Oktober", "November", "Dezember" + ) + } + val years = remember(today) { (today.year - 1..today.year + 20).toList() } + + var monthExpanded by remember { mutableStateOf(false) } + var yearExpanded by remember { mutableStateOf(false) } + + AlertDialog( + onDismissRequest = onDismiss, + title = { Text("Ablaufdatum wählen") }, + text = { + Row( + modifier = Modifier.fillMaxWidth(), + horizontalArrangement = Arrangement.spacedBy(8.dp) + ) { + ExposedDropdownMenuBox( + expanded = monthExpanded, + onExpandedChange = { monthExpanded = it }, + modifier = Modifier.weight(1f) + ) { + OutlinedTextField( + value = monthNames[selectedMonth - 1], + onValueChange = {}, + readOnly = true, + label = { Text("Monat") }, + trailingIcon = { ExposedDropdownMenuDefaults.TrailingIcon(expanded = monthExpanded) }, + modifier = Modifier.menuAnchor(MenuAnchorType.PrimaryNotEditable) + ) + ExposedDropdownMenu( + expanded = monthExpanded, + onDismissRequest = { monthExpanded = false } + ) { + monthNames.forEachIndexed { index, name -> + DropdownMenuItem( + text = { Text(name) }, + onClick = { + selectedMonth = index + 1 + monthExpanded = false + } + ) + } + } + } + + ExposedDropdownMenuBox( + expanded = yearExpanded, + onExpandedChange = { yearExpanded = it }, + modifier = Modifier.weight(1f) + ) { + OutlinedTextField( + value = selectedYear.toString(), + onValueChange = {}, + readOnly = true, + label = { Text("Jahr") }, + trailingIcon = { ExposedDropdownMenuDefaults.TrailingIcon(expanded = yearExpanded) }, + modifier = Modifier.menuAnchor(MenuAnchorType.PrimaryNotEditable) + ) + ExposedDropdownMenu( + expanded = yearExpanded, + onDismissRequest = { yearExpanded = false } + ) { + years.forEach { year -> + DropdownMenuItem( + text = { Text(year.toString()) }, + onClick = { + selectedYear = year + yearExpanded = false + } + ) + } + } + } + } + }, + confirmButton = { + TextButton(onClick = { onConfirm(YearMonth.of(selectedYear, selectedMonth)) }) { + Text("OK") + } + }, + dismissButton = { + Row { + TextButton(onClick = onRemove) { + Text("Entfernen") + } + TextButton(onClick = onDismiss) { + Text("Abbrechen") + } + } + } + ) +} diff --git a/app/src/main/java/de/krisenvorrat/app/ui/item/ItemListScreen.kt b/app/src/main/java/de/krisenvorrat/app/ui/item/ItemListScreen.kt index 9b44a94..d34373c 100644 --- a/app/src/main/java/de/krisenvorrat/app/ui/item/ItemListScreen.kt +++ b/app/src/main/java/de/krisenvorrat/app/ui/item/ItemListScreen.kt @@ -33,6 +33,7 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember +import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier @@ -231,18 +232,14 @@ private fun ItemCard( @Composable private fun ExpiryDateText(item: ItemUiModel) { - val formatter = DateTimeFormatter.ofPattern("dd.MM.yyyy") + val formatter = remember { DateTimeFormatter.ofPattern("MM/yyyy") } val formattedDate = item.expiryDate?.format(formatter).orEmpty() val color = when { item.isExpired -> MaterialTheme.colorScheme.error item.isExpiringSoon -> MaterialTheme.colorScheme.tertiary else -> MaterialTheme.colorScheme.onSurfaceVariant } - val prefix = when { - item.isExpired -> "Abgelaufen: " - item.isExpiringSoon -> "MHD: " - else -> "MHD: " - } + val prefix = if (item.isExpired) "Abgelaufen: " else "MHD: " Text( text = "$prefix$formattedDate", style = MaterialTheme.typography.bodySmall,