feat(item): DatePicker durch Monat/Jahr-Picker ersetzen
- Ablaufdatum wird jetzt als MM/yyyy gespeichert (letzter Tag des Monats) - DatePickerDialog -> AlertDialog mit zwei Dropdowns (Monat, Jahr) - Anzeige in ItemListScreen ebenfalls auf MM/yyyy umgestellt
This commit is contained in:
parent
fcc7142ea1
commit
b7f27b6f81
2 changed files with 136 additions and 50 deletions
|
|
@ -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)
|
||||
if (isPickerVisible) {
|
||||
MonthYearPickerDialog(
|
||||
initialDate = expiryDate,
|
||||
onConfirm = { yearMonth ->
|
||||
onDateSelected(yearMonth.atEndOfMonth())
|
||||
isPickerVisible = false
|
||||
},
|
||||
onRemove = {
|
||||
onDateSelected(null)
|
||||
isPickerVisible = false
|
||||
},
|
||||
onDismiss = { isPickerVisible = false }
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
@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 = {
|
||||
TextButton(onClick = {
|
||||
onDateSelected(null)
|
||||
isDatePickerVisible = false
|
||||
}) {
|
||||
Row {
|
||||
TextButton(onClick = onRemove) {
|
||||
Text("Entfernen")
|
||||
}
|
||||
}
|
||||
) {
|
||||
DatePicker(state = datePickerState)
|
||||
TextButton(onClick = onDismiss) {
|
||||
Text("Abbrechen")
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
Loading…
Reference in a new issue