package net.tinsae.clocked.components 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.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.width import androidx.compose.foundation.text.KeyboardOptions import androidx.compose.material3.Button import androidx.compose.material3.DatePicker import androidx.compose.material3.DatePickerDialog import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.MaterialTheme import androidx.compose.material3.ModalBottomSheet import androidx.compose.material3.OutlinedTextField import androidx.compose.material3.Text import androidx.compose.material3.TextButton import androidx.compose.material3.rememberDatePickerState import androidx.compose.material3.rememberModalBottomSheetState import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.input.KeyboardType import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import kotlinx.datetime.TimeZone import kotlinx.datetime.toLocalDateTime import net.tinsae.clocked.R import net.tinsae.clocked.data.EntryType import net.tinsae.clocked.ui.theme.ClockedTheme import kotlin.time.Clock import kotlin.time.Duration import kotlin.time.Duration.Companion.minutes import kotlin.time.Instant @OptIn(ExperimentalMaterial3Api::class) @Composable fun AddLogDialog( type: EntryType, onDismiss: () -> Unit, onSave: (timestamp: Instant, duration: Duration, reason: String?) -> Unit ) { val sheetState = rememberModalBottomSheetState() var hours by remember { mutableStateOf("") } var minutes by remember { mutableStateOf("") } var reason by remember { mutableStateOf("") } var selectedInstant by remember { mutableStateOf(Clock.System.now()) } var showDatePicker by remember { mutableStateOf(false) } // Custom date formatting logic using kotlinx-datetime val localDateTime = selectedInstant.toLocalDateTime(TimeZone.currentSystemDefault()) val formattedDate = "${localDateTime.month.name.take(3).lowercase().replaceFirstChar { it.uppercase() }} ${localDateTime.day}, ${localDateTime.year}" if (showDatePicker) { val datePickerState = rememberDatePickerState(initialSelectedDateMillis = selectedInstant.toEpochMilliseconds()) DatePickerDialog( onDismissRequest = { showDatePicker = false }, confirmButton = { TextButton(onClick = { datePickerState.selectedDateMillis?.let { millis -> selectedInstant = Instant.fromEpochMilliseconds(millis) } showDatePicker = false }) { Text(stringResource(id = R.string.ok)) } }, dismissButton = { TextButton(onClick = { showDatePicker = false }) { Text(stringResource(id = R.string.cancel)) } } ) { DatePicker(state = datePickerState) } } ModalBottomSheet( onDismissRequest = onDismiss, sheetState = sheetState ) { Column( modifier = Modifier .padding(16.dp) .fillMaxWidth(), verticalArrangement = Arrangement.spacedBy(16.dp) ) { Text( text = if (type == EntryType.OVERTIME) stringResource(id = R.string.add_overtime_title) else stringResource(id = R.string.add_time_off_title), style = MaterialTheme.typography.titleLarge ) // Date Row Row( modifier = Modifier.fillMaxWidth(), verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.SpaceBetween ) { Text(stringResource(id = R.string.date), style = MaterialTheme.typography.bodyLarge) TextButton( onClick = { showDatePicker = true }, ) { Text(formattedDate, style = MaterialTheme.typography.bodyLarge) } } // Duration Row Text( text = stringResource(id = R.string.duration), style = MaterialTheme.typography.bodyLarge, ) Row( modifier = Modifier.fillMaxWidth(), verticalAlignment = Alignment.CenterVertically ) { OutlinedTextField( value = hours, onValueChange = { hours = it.filter(Char::isDigit) }, label = { Text(stringResource(id = R.string.hours)) }, keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number), modifier = Modifier.weight(0.5f) ) Spacer(modifier = Modifier.width(8.dp)) OutlinedTextField( value = minutes, onValueChange = { val filtered = it.filter(Char::isDigit) if (filtered.isEmpty() || filtered.toInt() < 60) { minutes = filtered } }, label = { Text(stringResource(id = R.string.minutes)) }, keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number), modifier = Modifier.weight(0.5f) ) } // Reason Field OutlinedTextField( value = reason, onValueChange = { reason = it }, label = { Text(stringResource(id = R.string.reason)) }, modifier = Modifier.fillMaxWidth() ) // Action Buttons Row( modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.End, ) { TextButton(onClick = onDismiss) { Text(stringResource(id = R.string.cancel)) } Spacer(modifier = Modifier.width(8.dp)) Button( onClick = { val h = hours.toLongOrNull() ?: 0L val m = minutes.toLongOrNull() ?: 0L val totalMinutes = if (type == EntryType.OVERTIME) h * 60 + m else -(h * 60 + m) val duration = totalMinutes.minutes onSave(selectedInstant, duration, reason.takeIf(String::isNotBlank)) } ) { Text(stringResource(id = R.string.save)) } } } } } @Preview(showBackground = true) @Composable fun AddLogDialogPreview(){ ClockedTheme { AddLogDialog( type = EntryType.OVERTIME, onDismiss = {}, onSave = { _, _, _ -> } ) } }